Setup

We will clean the environment, setup the locations, define colors, and create a datestamp.

Clean the environment.

Set locations and working directories…


Create a new analysis directory...
[1] FALSE
[1] FALSE
[1] FALSE
[1] FALSE
[1] FALSE
[1] FALSE
[1] "/Users/swvanderlaan/PLINK/analyses/consortia/CHARGE_1000G_CAC"
 [1] "_archived"                                   "1. CHARGE_1000G_CAC.nb.html"                 "1. CHARGE_1000G_CAC.Rmd"                    
 [4] "2. bulkRNAseq.nb.html"                       "2. bulkRNAseq.Rmd"                           "20220128.CAC.RegionalAssociationPlots.RData"
 [7] "20220129.CAC.Parsing_GWASSumStats.RData"     "20220131.CAC.PolarMorphism.RData"            "20220201.CAC.PolarMorphism.RData"           
[10] "3. scRNAseq.nb.html"                         "3. scRNAseq.Rmd"                             "4. Parsing_GWASSumStats.nb.html"            
[13] "4. Parsing_GWASSumStats.Rmd"                 "5. RegionalAssociationPlots.nb.html"         "5. RegionalAssociationPlots.Rmd"            
[16] "6. PolarMorphism.nb.html"                    "6. PolarMorphism.Rmd"                        "6. PolarMorphismbeta.nb.html"               
[19] "6. PolarMorphismbeta.Rmd"                    "CAC"                                         "CHARGE_1000G_CAC.Rproj"                     
[22] "CredibleSets"                                "images"                                      "LICENSE"                                    
[25] "PolarMorphism"                               "RACER"                                       "README.html"                                
[28] "README.md"                                   "README.orig.md"                              "renv"                                       
[31] "renv.lock"                                   "scripts"                                     "SNP"                                        
[34] "targets"                                    

… a package-installation function …

source(paste0(PROJECT_loc, "/scripts/functions.R"))

… and load those packages.

install.packages.auto("readr")
install.packages.auto("optparse")
install.packages.auto("tools")
install.packages.auto("dplyr")
install.packages.auto("tidyr")
install.packages.auto("naniar")

# To get 'data.table' with 'fwrite' to be able to directly write gzipped-files
# Ref: https://stackoverflow.com/questions/42788401/is-possible-to-use-fwrite-from-data-table-with-gzfile
# install.packages("data.table", repos = "https://Rdatatable.gitlab.io/data.table")
library(data.table)
install.packages.auto("tidyverse")
install.packages.auto("DT")

install.packages.auto("knitr")
install.packages.auto("eeptools")

install.packages.auto("haven")
install.packages.auto("tableone")

install.packages.auto("BlandAltmanLeh")

# Install the devtools package from Hadley Wickham
install.packages.auto('devtools')
library(devtools) 

# for plotting
install.packages.auto("pheatmap")
install.packages.auto("forestplot")
install.packages.auto("ggplot2")
install.packages.auto("ggpubr")
install.packages.auto("ggrepel")

install.packages.auto("UpSetR")

devtools::install_github("thomasp85/patchwork")
Using github PAT from envvar GITHUB_PAT
Skipping install of 'patchwork' from a github remote, the SHA1 (79223d30) has not changed since last install.
  Use `force = TRUE` to force installation
# For regional association plots
install_github("oliviasabik/RACER") 
Using github PAT from envvar GITHUB_PAT
Skipping install of 'RACER' from a github remote, the SHA1 (1394c9d4) has not changed since last install.
  Use `force = TRUE` to force installation
# Install ggrepel package if needed
# install.packages.auto(ggrepel)
library(ggrepel)

install.packages.auto(qvalue)
Loading required package: qvalue

We will create a datestamp and define the Utrecht Science Park Colour Scheme.


Today = format(as.Date(as.POSIXlt(Sys.time())), "%Y%m%d")
Today.Report = format(as.Date(as.POSIXlt(Sys.time())), "%A, %B %d, %Y")

### UtrechtScienceParkColoursScheme
###
### WebsitetoconvertHEXtoRGB:http://hex.colorrrs.com.
### Forsomefunctionsyoushoulddividethesenumbersby255.
###
### No. Color                 HEX   (RGB)                                     CHR         MAF/INFO
###---------------------------------------------------------------------------------------
### 1     yellow                #FBB820 (251,184,32)                      =>    1       or 1.0>INFO
### 2     gold                #F59D10 (245,157,16)                    =>    2       
### 3     salmon                #E55738 (229,87,56)                   =>    3       or 0.05<MAF<0.2 or 0.4<INFO<0.6
### 4     darkpink          #DB003F ((219,0,63)                   =>    4       
### 5     lightpink         #E35493 (227,84,147)                      =>    5       or 0.8<INFO<1.0
### 6     pink                #D5267B (213,38,123)                    =>    6       
### 7     hardpink          #CC0071 (204,0,113)                   =>    7       
### 8     lightpurple       #A8448A (168,68,138)                      =>    8       
### 9     purple                #9A3480 (154,52,128)                      =>    9       
### 10  lavendel            #8D5B9A (141,91,154)                      =>    10      
### 11  bluepurple        #705296 (112,82,150)                    =>    11      
### 12  purpleblue        #686AA9 (104,106,169)               =>    12      
### 13  lightpurpleblue #6173AD (97,115,173/101,120,180)    =>  13      
### 14  seablue             #4C81BF (76,129,191)                      =>    14      
### 15  skyblue             #2F8BC9 (47,139,201)                      =>    15      
### 16  azurblue            #1290D9 (18,144,217)                      =>    16      or 0.01<MAF<0.05 or 0.2<INFO<0.4
### 17  lightazurblue     #1396D8 (19,150,216)                    =>    17      
### 18  greenblue           #15A6C1 (21,166,193)                      =>    18      
### 19  seaweedgreen      #5EB17F (94,177,127)                    =>    19      
### 20  yellowgreen       #86B833 (134,184,51)                    =>    20      
### 21  lightmossgreen  #C5D220 (197,210,32)                      =>    21      
### 22  mossgreen           #9FC228 (159,194,40)                      =>    22      or MAF>0.20 or 0.6<INFO<0.8
### 23  lightgreen      #78B113 (120,177,19)                      =>    23/X
### 24  green                 #49A01D (73,160,29)                     =>    24/Y
### 25  grey                  #595A5C (89,90,92)                        =>  25/XY   or MAF<0.01 or 0.0<INFO<0.2
### 26  lightgrey           #A2A3A4 (162,163,164)                 =>    26/MT
###
### ADDITIONAL COLORS
### 27  midgrey         #D7D8D7
### 28  verylightgrey   #ECECEC"
### 29  white           #FFFFFF
### 30  black           #000000
###----------------------------------------------------------------------------------------------

uithof_color = c("#FBB820","#F59D10","#E55738","#DB003F","#E35493","#D5267B",
                 "#CC0071","#A8448A","#9A3480","#8D5B9A","#705296","#686AA9",
                 "#6173AD","#4C81BF","#2F8BC9","#1290D9","#1396D8","#15A6C1",
                 "#5EB17F","#86B833","#C5D220","#9FC228","#78B113","#49A01D",
                 "#595A5C","#A2A3A4", "#D7D8D7", "#ECECEC", "#FFFFFF", "#000000")

uithof_color_legend = c("#FBB820", "#F59D10", "#E55738", "#DB003F", "#E35493",
                        "#D5267B", "#CC0071", "#A8448A", "#9A3480", "#8D5B9A",
                        "#705296", "#686AA9", "#6173AD", "#4C81BF", "#2F8BC9",
                        "#1290D9", "#1396D8", "#15A6C1", "#5EB17F", "#86B833",
                        "#C5D220", "#9FC228", "#78B113", "#49A01D", "#595A5C",
                        "#A2A3A4", "#D7D8D7", "#ECECEC", "#FFFFFF", "#000000")
### ----------------------------------------------------------------------------

Introduction

We will apply PolarMorphism to compare the CAC GWAS vs cIMT, CAD, and ischemic stroke subtypes.


library(devtools)
install_github("UMCUgenetics/PolarMorphism")
Using github PAT from envvar GITHUB_PAT
Downloading GitHub repo UMCUgenetics/PolarMorphism@HEAD
  
   checking for file ‘/private/var/folders/cj/1vxt4xb11m1cx7wn020f8hww0000gn/T/RtmpQGmRRV/remotes1cc0284d0cfc/UMCUGenetics-PolarMorphism-1d1bfd7/DESCRIPTION’ ...
  
✓  checking for file ‘/private/var/folders/cj/1vxt4xb11m1cx7wn020f8hww0000gn/T/RtmpQGmRRV/remotes1cc0284d0cfc/UMCUGenetics-PolarMorphism-1d1bfd7/DESCRIPTION’ (342ms)

  
─  preparing ‘PolarMorphism’:
✓  checking DESCRIPTION meta-information

  
─  excluding invalid files

  
   Subdirectory 'R' contains invalid file names:
     ‘kappas.4foldtransform.Rda’

  
─  checking for LF line-endings in source and make files and shell scripts

  
─  checking for empty or unneeded directories
   Omitted ‘LazyData’ from DESCRIPTION

  
     NB: this package now depends on R (>= 3.5.0)
     WARNING: Added dependency on R >= 3.5.0 because serialized objects in
     serialize/load version 3 cannot be read in older versions of R.
     File(s) containing such objects:
       ‘PolarMorphism/R/sysdata.rda’
─  building ‘PolarMorphism_0.0.0.9000.tar.gz’

  
   
Installing package into ‘/Users/swvanderlaan/Library/R/x86_64/4.1/library’
(as ‘lib’ is unspecified)
* installing *source* package ‘PolarMorphism’ ...
** using staged installation
** R
** byte-compile and prepare package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded from temporary location
** testing if installed package can be loaded from final location
** testing if installed package keeps a record of temporary installation path
* DONE (PolarMorphism)
library(PolarMorphism)

Loading and parsing data

We need to load and parse the data first.

We used gwas2cojo to parse the datasets with 1000G phase 3 as a reference. This resulted in cleaned and harmonized GWAS datasets with standardized headings, and containing rsIDs that are also present in 1000G. For some GWAS studies, missing beta or se were recovered from z-scores or missing chr:bp positions/frequencies from rsids using the dbSNP153 COMMON database. Prior to conversion, non-biallelic SNPs were removed from the genomic reference. Therefore, some triallelic SNPs may not be present.

Phenotype                       Short       Source File                                                                 PMID
Coronary calcification          CAC         
Coronary artery disease         CAD         UKBB.GWAS1KG.EXOME.CAD.SOFT.META.PublicRelease.300517.txt.gz                28714975

Any stroke                      AS          MEGASTROKE.1.AS.EUR.GC_filtered_X_nocases_het.txt.gz                        29531354
Any ischemic stroke             IS          MEGASTROKE.2.IS.EUR.GC_filtered_X_nocases_het.txt.gz                        29531354
Large artery stroke             LAS         MEGASTROKE.3.LAS.EUR.GC_filtered_X_nocases_het.txt.gz                       29531354
Cardio-embolic stroke           CES         MEGASTROKE.4.CE.EUR.GC_filtered_X_nocases_het.txt.gz                        29531354
Small vessel disease            SVD         MEGASTROKE.5.SVD.EUR.GC_filtered_X_nocases_het.txt.gz                       29531354

Carotid IMT                     cIMT        IMT.EA.META.MAF1.HetDF4_jun.csv.gz                                          30586722
Plaque presence                 Plaque      Plaque_meta_032218.csv.gz                                                   30586722

GWAS datasets

COJO_loc = paste0(GWAS_loc, "/_cojo/rsid")

# traits <- c("CAD", "AS", "IS", "LAS", "CES", "SVD", "cIMT", "Plaque")
traits <- c("CAD")

for(trait in traits){
  cat(paste0("\nProcessing data for [", trait, "] (",paste0(COJO_loc, "/", trait, ".cojo.gz"),").\n"))
  
  tbl <- as_tibble(fread(paste0(COJO_loc, "/", trait, ".cojo.gz"),
                         verbose = FALSE,
                         showProgress = TRUE,
                         nThread = 4))
  # the column names are "SNP"  "A1"   "A2"   "freq" "b"    "se"   "p"    "n"
  # we have to change them so PolarMorphism knows what each column contains
  colnames(tbl) <- c("snpid","a1","a2","freq","beta","se","pval", "n") # note that PolarMorphism does not need or use the "n" column
  assign(trait, tbl)

}

Processing data for [CAD] (/Users/swvanderlaan//PLINK/_GWAS_Datasets/_cojo/rsid/CAD.cojo.gz).
|--------------------------------------------------|
|==================================================|
rm(tbl)

Coronary artery calcification

cat("\nGWAS: coronary artery calcification, CAC:\n")

GWAS: coronary artery calcification, CAC:
gwas_sumstats <- readRDS(file = paste0(OUT_loc, "/gwas_sumstats_complete.rds"))
head(gwas_sumstats)
nrow(gwas_sumstats)
[1] 8586047
library(tidyverse)

CACtib <- as_tibble(gwas_sumstats %>% 
                      select(rsID, Chr, Position, Allele1, Allele2, Freq1, Effect, SE, Pvalue, N) %>% # end of select
                      mutate(Allele1 = toupper(Allele1)) %>% # change all characters to uppercase
                      mutate(Allele2 = toupper(Allele2)) %>% 
                      rename( # rename variables
                      BP = Position, # new vs old
                      snpid = rsID,
                      a1 = Allele1, 
                      a2 = Allele2, 
                      freq = Freq1, 
                      beta = Effect,
                      se = SE,
                      pval = Pvalue,
                      n = N
                      ) %>% # end of rename
                      relocate(snpid, Chr, BP, a1, a2, freq, beta, se, pval, n) # set the order of columns
                    )
head(CACtib)
str(CACtib)
tibble [8,586,047 × 10] (S3: tbl_df/tbl/data.frame)
 $ snpid: chr [1:8586047] "rs61769339" "rs61769350" "." "rs189800799" ...
 $ Chr  : int [1:8586047] 1 1 1 1 1 1 1 1 1 1 ...
 $ BP   : int [1:8586047] 662622 693731 697411 701835 706368 712930 713131 714019 714596 715265 ...
 $ a1   : chr [1:8586047] "A" "A" "D" "T" ...
 $ a2   : chr [1:8586047] "G" "G" "I" "C" ...
 $ freq : num [1:8586047] 0.114 0.871 0.969 0.969 0.644 ...
 $ beta : num [1:8586047] 0.1104 -0.0377 0.0305 -0.1051 0.1408 ...
 $ se   : num [1:8586047] 0.0791 0.0691 0.196 0.1573 0.0862 ...
 $ pval : num [1:8586047] 0.163 0.585 0.876 0.504 0.102 ...
 $ n    : int [1:8586047] 8357 8781 4445 5878 3074 6709 6709 5654 7998 7998 ...
 - attr(*, ".internal.selfref")=<externalptr> 
nrow(CACtib)
[1] 8586047
rm(gwas_sumstats)

Saving

Let’s save this work for easy access.


saveRDS(CACtib, file = paste0(OUT_loc, "/", Today, ".CAC.rds"))
saveRDS(CAD, file = paste0(OUT_loc, "/", Today, ".CAD.rds"))

# rm(CACtib, CAD)
# 
# saveRDS(CIMT, file = paste0(OUT_loc, "/", Today, ".CIMT.rds"))
# saveRDS(PLAQ, file = paste0(OUT_loc, "/", Today, ".PLAQ.rds"))
# 
# rm(CIMT, PLAQ)
# 
# saveRDS(AS, file = paste0(OUT_loc, "/", Today, ".AS.rds"))
# saveRDS(IS, file = paste0(OUT_loc, "/", Today, ".IS.rds"))
# saveRDS(LAS, file = paste0(OUT_loc, "/", Today, ".LAS.rds"))
# saveRDS(CE, file = paste0(OUT_loc, "/", Today, ".CE.rds"))
# saveRDS(SVD, file = paste0(OUT_loc, "/", Today, ".SVD.rds"))
# 
# rm(AS, IS, LAS, CE, SVD)

Loading for easy access


CACtib <- readRDS(file = paste0(OUT_loc, "/20220131.CAC.rds"))
CADtib <- readRDS(file = paste0(OUT_loc, "/20220131.CAD.rds"))

# CIMTtib <- readRDS(file = paste0(OUT_loc, "/20220131.CIMT.rds"))
# PLAQtib <- readRDS(file = paste0(OUT_loc, "/20220131.PLAQ.rds"))
# 
# AStib <- readRDS(file = paste0(OUT_loc, "/20220131.AS.rds"))
# IStib <- readRDS(file = paste0(OUT_loc, "/20220131.IS.rds"))
# LAStib <- readRDS(file = paste0(OUT_loc, "/20220131.LAS.rds"))
# CEtib <- readRDS(file = paste0(OUT_loc, "/20220131.CE.rds"))
# SVDtib <- readRDS(file = paste0(OUT_loc, "/20220131.SVD.rds"))

PolarMorphize

We need to choose one of the GWAS as reference to make sure all GWASs have the same reference and alternative allele for each SNP. We will make CAC the reference, and ‘flip’ the alleles of all the GWASs so they align with CAC.

Clean up CAC GWAS

There are some variants without ‘rsID’, we are going to remove these.


CACtibf <- CACtib %>% 
  filter(snpid!='.') %>% # end of rename
                      relocate(snpid, beta, se, a1, a2, freq, pval, Chr, BP, n) # set the order of columns
rm(CACtib)

We will make the list of SNPs for alignment.

variants_of_interest <- CACtibf$snpid[grepl("rs", CACtibf$snpid)]

Because the function Alleleflip not only flips the alleles, but also adds a z-score column, we have to manually do that for CAC.


CACtibf$z <- CACtibf$beta/CACtibf$se

Aligning Alleles

CADtib <- PolarMorphism::AlleleFlip(sumstats = CAD, snps = CACtibf %>% select(snpid, a1, a2), snpid = "snpid", only.a2 = F)
rm(CAD)

Converting to polar

Now we are converting GWAS to polar-coordinates.


CAC_CADtibf <- PolarMorphism::ConvertToPolar(dfnames = c("CACtibf", "CADtib"), # not clear how this should be given
                                             snpid = "snpid",
                                             whiten = TRUE,
                                             covsnps = variants_of_interest, # is this required?
                                             mahalanobis.threshold = 5,
                                             whitening.method = "ZCA-cor",
                                             LDcorrect = FALSE, # we don't need that as we de-correlate through whitening
                                             ld.path = paste0(PROJECT_loc,"/eur_w_ld_chr/")) 
[1] "number of SNPs used for cov: 6988432"
head(CAC_CADtibf)
str(CAC_CADtibf)
tibble [6,993,034 × 19] (S3: tbl_df/tbl/data.frame)
 $ snpid       : chr [1:6993034] "rs143225517" "rs2073813" "rs3131969" "rs3131968" ...
 $ Chr         : int [1:6993034] 1 1 1 1 1 1 1 1 1 1 ...
 $ BP          : int [1:6993034] 751756 753541 754182 754192 754334 755890 756604 757640 757734 757936 ...
 $ beta.1      : num [1:6993034] -0.0001 0.0036 0.0002 0.0005 0.0058 0.0032 0.0029 -0.0167 -0.0069 -0.0072 ...
 $ beta.2      : num [1:6993034] -0.00546 0.00184 0.00361 0.00373 0.005 0.00441 0.00162 -0.00343 -0.00684 -0.00728 ...
 $ se.1        : num [1:6993034] 0.0291 0.0309 0.0301 0.0302 0.0304 0.0288 0.0288 0.0328 0.0284 0.0285 ...
 $ se.2        : num [1:6993034] 0.0142 0.0138 0.0137 0.0137 0.0138 ...
 $ pval.1      : num [1:6993034] 0.998 0.906 0.996 0.987 0.85 ...
 $ pval.2      : num [1:6993034] 0.7 0.894 0.792 0.785 0.718 ...
 $ a1.1        : chr [1:6993034] "T" "A" "A" "A" ...
 $ a1.2        : chr [1:6993034] "T" "A" "A" "A" ...
 $ a1.b        : chr [1:6993034] "C" "A" "G" "G" ...
 $ a2.1        : chr [1:6993034] "C" "G" "G" "G" ...
 $ a2.2        : chr [1:6993034] "C" "G" "G" "G" ...
 $ a2.b        : chr [1:6993034] "T" "G" "A" "A" ...
 $ r           : num [1:6993034] 0.372 0.162 0.254 0.262 0.379 ...
 $ angle       : num [1:6993034] -0.1456 -2.8408 -0.081 -0.0606 -1.8259 ...
 $ z.whitened.1: num [1:6993034] 0.01355 0.10594 -0.00515 0.00397 0.16721 ...
 $ z.whitened.2: num [1:6993034] -0.372 0.123 0.254 0.262 0.341 ...
 - attr(*, ".internal.selfref")=<externalptr> 

Polar plotting

temp <- CAC_CADtibf %>%
  filter(r > 4)

# set the limits
max_z_1 <- max(temp$z.whitened.1)
max_z_2 <- max(temp$z.whitened.2)
lim <- round(
  ifelse(max_z_2 < max_z_1, max_z_1, max_z_2),
  digits = 0)

rm(max_z_1, max_z_2)
gwas_z <- abs(qnorm(5e-8))

ggpubr::ggscatter(temp,
                  x = "z.whitened.1",
                  y = "z.whitened.2",
                  color = uithof_color[16], 
                  xlim = c(-lim,lim),
                  ylim = c(-lim,lim),
                  xlab = "CAC (z, whitened)",
                  ylab = "CAD (z, whitened)") + 
  geom_hline(yintercept = gwas_z, linetype = 2,
                color = uithof_color[3], size = 0.5) +
  geom_hline(yintercept = -gwas_z, linetype = 2,
                color = uithof_color[3], size = 0.5) +
  geom_vline(xintercept = gwas_z, linetype = 2,
                color = uithof_color[3], size = 0.5) +
  geom_vline(xintercept = -gwas_z, linetype = 2,
                color = uithof_color[3], size = 0.5) +
  theme(aspect.ratio = 1) +
  theme_minimal()

rm(temp)

We calculated the central angle between observed and expected SNP position under the null-hypothesis of trait-specific effects, and the radius (r) indicating the size of the effect.

Now we calculate the p-value for the radius, r.

library(qvalue)
# p-value & q-value for r
CAC_CADtibf$r.pval <- PolarMorphism::PvalueForR(r = CAC_CADtibf$r, 
                                                p = 2)
CAC_CADtibf$r.qval <- qvalue(p = CAC_CADtibf$r.pval)$qvalues

Second, we calculate the the Von Mises distribution p-value for the angle, theta, but only for the subset of variants where the false-discovery rate q-value is significant, q < 0.05.

# filter on r, for p-value & q-value for theta

PolarMorphies <- CAC_CADtibf[CAC_CADtibf$r.qval < 0.05,]

PolarMorphies$theta.pval <- PolarMorphism::PvalueForAngle(angle.trans = PolarMorphies$angle, 
                                                          r = PolarMorphies$r,
                                                          # tol = 1e-20,
                                                          kappa.file = paste0(PROJECT_loc, "/PolarMorphism/kappas.4foldtransform.Rda"),
                                                          debug = FALSE)
# some theta.pval are smaller than 0
PolarMorphies$theta.pval[PolarMorphies$theta.pval < 0] <- 0 

# min(PolarMorphies$theta.pval)

PolarMorphies$theta.qval <- qvalue(p = PolarMorphies$theta.pval)$qvalues

PolarMorphies <- PolarMorphies %>%
  mutate(ThetaQ = case_when(theta.qval < 0.05 & pval.1 > 5e-8 & pval.2 > 5e-8~ 'novel',
                            theta.qval < 0.05 & pval.1 < 5e-8 & pval.2 > 5e-8~ 'sign. GWAS CAC',
                            theta.qval < 0.05 & pval.1 > 5e-8 & pval.2 < 5e-8~ 'sign. GWAS CAD',
                            TRUE ~ 'not significant'))
# filter on theta
PolarMorphies %>%
  ggplot(aes(x = abs(angle), y = r, color = theta.qval < 0.05)) +
  geom_point()



PolarMorphies %>%
  ggplot(aes(x = abs(z.whitened.1), y = abs(z.whitened.2), color = theta.qval < 0.05)) +
  theme(aspect.ratio = 1) +
  xlim(0,lim) +
  ylim(0,lim) +
  geom_point()
Warning: Removed 2 rows containing missing values (geom_point).

PolarMorphies$abs.z.whitened.1 <- abs(PolarMorphies$z.whitened.1)
PolarMorphies$abs.z.whitened.2 <- abs(PolarMorphies$z.whitened.2)

ggpubr::ggscatter(PolarMorphies,
                  x = "abs.z.whitened.1",
                  y = "abs.z.whitened.2",
                  color = "ThetaQ", palette = "npg",
                  xlim = c(0,lim),
                  ylim = c(0,lim),
                  xlab = "CAC\n(z, whitened, abs)",
                  ylab = "CAD\n(z, whitened, abs)") + 
  geom_hline(yintercept = gwas_z, linetype = 2,
                color = uithof_color[3], size = 0.5) +
  geom_vline(xintercept = gwas_z, linetype = 2,
                color = uithof_color[3], size = 0.5) +
  theme(aspect.ratio = 1) +
  theme_minimal()

NA
NA
NA
PolarMorphies

Prune significant variants


# fix so that ieugwasr::ld_clump will work
PolarMorphies <- rename(PolarMorphies, rsid = snpid)
PolarMorphies <- rename(PolarMorphies, pval = theta.qval) # we want to clump on the theta.qval

PolarMorphiesLDclump <- ieugwasr::ld_clump(
  dat = PolarMorphies, 
  clump_kb = 10000, 
  clump_r2 = 0.001, 
  clump_p = 1, 
  pop = "EUR", 
  access_token = NULL
  # access_token = NULL,
  # bfile = NULL,
  # plink_bin = NULL
  )
Please look at vignettes for options on running this locally if you need to run many instances of this command.
Clumping KssNjI, 8636 variants, using EUR population reference
Removing 8450 of 8636 variants due to LD with other variants or absence from LD reference panel
# fix so that PolarMorphies are correct again
PolarMorphies <- rename(PolarMorphies, snpid = rsid)
PolarMorphies <- rename(PolarMorphies, theta.qval = pval)

PolarMorphiesLDclump <- rename(PolarMorphiesLDclump, snpid = rsid)
PolarMorphiesLDclump <- rename(PolarMorphiesLDclump, theta.qval = pval)

PolarMorphiesLDclump
NA

Annotation


# Reference: https://bioinformatics.stackexchange.com/questions/2503/how-to-get-a-list-of-genes-corresponding-to-the-list-of-snps-rs-ids/2504

require(biomaRt)

# To show which marts are available
listMarts()
Ensembl site unresponsive, trying useast mirror
# You need the SNP mart
mart <- useMart("ENSEMBL_MART_SNP")

# Find homo sapiens
listDatasets(mart)
Ensembl site unresponsive, trying uswest mirror
# This will be the dataset we want to use
dataset <- useDataset("hsapiens_snp", mart = mart)

# Show available filters
listFilters(dataset)

# Now list all available attributes
listAttributes(dataset)

# You need the SNP mart from homo sapiens
ensembl <- useMart("ENSEMBL_MART_SNP", dataset = "hsapiens_snp")
# To get the ensembl gene id belonging to the SNPs
Annotations <- getBM(attributes = c("refsnp_id", "chr_name", "chrom_start", "chrom_end",
                                                  "allele", "mapweight", "validated", "allele_1", "minor_allele",
                                                  "minor_allele_freq", "minor_allele_count", "clinical_significance",
                                                  "synonym_name", "refsnp_source", "ensembl_gene_stable_id", "ensembl_gene_name"),
                                   filters = "snp_filter", values = PolarMorphiesLDclump$snpid,
                                   mart = ensembl, 
                                   uniqueRows = TRUE)
Annotations
NA
fwrite(PolarMorphiesLDclump, file = paste0(OUT_loc, "/PolarMorphiesLDclump.txt"), sep = "\t")
proxySearch_CAC_CAD <- fread(file = paste0(PROJECT_loc, "/PolarMorphism/proxySearch_CAC_CAD/proxySearch.results.csv"))

Plotting


novel_snps <- PolarMorphiesLDclump$snpid[grepl("novel", PolarMorphiesLDclump$ThetaQ)]

library(ggpubr)
p1 <- ggpubr::ggscatter(PolarMorphiesLDclump,
                  x = "abs.z.whitened.1",
                  y = "abs.z.whitened.2",
                  color = "ThetaQ", palette = "npg",
                  xlim = c(0,lim),
                  ylim = c(0,lim),
                  xlab = "CAC\n(z, whitened, abs)",
                  ylab = "CAD\n(z, whitened, abs)",
                  title = "CAC vs CAD",
                  label = "snpid",
                  label.select = novel_snps,
                  repel = TRUE, show.legend.text = FALSE) + 
  geom_hline(yintercept = gwas_z, linetype = 2,
                color = uithof_color[3], size = 0.5) +
  geom_vline(xintercept = gwas_z, linetype = 2,
                color = uithof_color[3], size = 0.5) +
  theme(aspect.ratio = 1) +
  theme_minimal() + theme(legend.position = "bottom")


p2 <- ggpubr::ggscatter(PolarMorphiesLDclump,
                  x = "abs.z.whitened.1",
                  y = "abs.z.whitened.2",
                  color = "ThetaQ", palette = "npg",
                  xlim = c(0,lim/3),
                  ylim = c(0,lim/3),
                  xlab = "CAC\n(z, whitened, abs)",
                  ylab = "CAD\n(z, whitened, abs)",
                  title = "CAC vs CAD (zoomed)",
                  label = "snpid",
                  label.select = novel_snps,
                  repel = TRUE, show.legend.text = FALSE) + 
  geom_hline(yintercept = gwas_z, linetype = 2,
                color = uithof_color[3], size = 0.5) +
  geom_vline(xintercept = gwas_z, linetype = 2,
                color = uithof_color[3], size = 0.5) +
  theme(aspect.ratio = 1) +
  theme_minimal() + theme(legend.position = "bottom")


p1
Warning: ggrepel: 140 unlabeled data points (too many overlaps). Consider increasing max.overlaps
ggsave(filename = paste0(PLOT_loc, "/", Today, ".PolarMorphies.LDclump.CAC.CAD.pdf"), plot = p1)
Saving 7.29 x 4.51 in image
Warning: ggrepel: 140 unlabeled data points (too many overlaps). Consider increasing max.overlaps
ggsave(filename = paste0(PLOT_loc, "/", Today, ".PolarMorphies.LDclump.CAC.CAD.png"), plot = p1)
Saving 7.29 x 4.51 in image
Warning: ggrepel: 140 unlabeled data points (too many overlaps). Consider increasing max.overlaps

p2
Warning: ggrepel: 135 unlabeled data points (too many overlaps). Consider increasing max.overlaps
ggsave(filename = paste0(PLOT_loc, "/", Today, ".PolarMorphies.LDclump.CAC.CAD.zoom.pdf"), plot = p2)
Saving 7.29 x 4.51 in image
Warning: ggrepel: 135 unlabeled data points (too many overlaps). Consider increasing max.overlaps
ggsave(filename = paste0(PLOT_loc, "/", Today, ".PolarMorphies.LDclump.CAC.CAD.zoom.png"), plot = p2)
Saving 7.29 x 4.51 in image
Warning: ggrepel: 135 unlabeled data points (too many overlaps). Consider increasing max.overlaps

install.packages.auto(plotly)
library(plotly)

p3 <- ggpubr::ggscatter(PolarMorphiesLDclump,
                  x = "abs.z.whitened.1",
                  y = "abs.z.whitened.2",
                  color = "ThetaQ", 
                  palette = "npg",
                  xlim = c(0,lim/3),
                  ylim = c(0,lim/3),
                  xlab = "CAC\n(z, whitened, abs)",
                  ylab = "CAD\n(z, whitened, abs)",
                  title = "CAC vs CAD (zoomed)") + 
  geom_hline(yintercept = gwas_z, linetype = 2,
                color = uithof_color[3], size = 0.5) +
  geom_vline(xintercept = gwas_z, linetype = 2,
                color = uithof_color[3], size = 0.5) +
  theme(aspect.ratio = 1) +
  theme_minimal() + theme(legend.position = "bottom")

ggplotly(p3, 
         source = "select", 
         tooltip = c("all"))
NA
NA
library(plotly)

# https://plotly.com/r/horizontal-vertical-shapes/

vline <- function(x = 0, color = uithof_color[3]) {
  list(
    type = "line",
    y0 = 0,
    y1 = 1,
    yref = "paper",
    x0 = x,
    x1 = x,
    line = list(color = color, dash="dot")
  )
}

hline <- function(y = 0, color = uithof_color[3]) {
  list(
    type = "line",
    x0 = 0,
    x1 = 1,
    xref = "paper",
    y0 = y,
    y1 = y,
    line = list(color = color, dash="dot")
  )
}

# https://plotly.com/r/hover-text-and-formatting/

fig <- PolarMorphiesLDclump %>%
  plot_ly(
    type = "scatter",
    mode = 'markers',
    x = ~abs.z.whitened.1, 
    y = ~abs.z.whitened.2,
    marker = list(size = ~r, sizeref = 0.8), 
    color = ~ThetaQ, colors = uithof_color,
    text = ~snpid,
    hovertemplate = paste(
      "<b>%{text}</b><br><br>",
      "%{yaxis.title.text}: %{y} (abs., whitened)<br>",
      "%{xaxis.title.text}: %{x} (abs., whitened)<br>",
      "<extra></extra>"
      )
    ) %>%
  layout(title = 'PolarMorphism: CAC vs CAD', 
         plot_bgcolor = "white",
         xaxis = list(title = "CAC"), 
         yaxis = list(title = "CAD"), 
         legend = list(title=list(text='<b>Pleiotropic effect </b>')),
         shapes = list(vline(gwas_z), hline(gwas_z) #,
                            # list(type = "rect",
                            #      fillcolor = uithof_color[28], line = list(color = uithof_color[27]), 
                            #      opacity = 0.2,
                            #      y0 = nom_z_y, y1 = gwas_z, x0 = nom_z_x, x1 = gwas_z)
                            # )
                       )
  )

fig <- fig %>%
  layout(legend = list(orientation = 'h', y = -0.3))

fig
NA

Session information


Version:      v1.0.0
Last update:  2022-02-03
Written by:   Sander W. van der Laan (s.w.vanderlaan-2[at]umcutrecht.nl).
Description:  Script to create plot regional association plots.
Minimum requirements: R version 3.4.3 (2017-06-30) -- 'Single Candle', Mac OS X El Capitan

Changes log
* v1.0.0 Initial version. 

sessionInfo()

Saving environment


save.image(paste0(PROJECT_loc, "/",Today,".",PROJECTNAME,".PolarMorphism.RData"))
© 1979-2022 Sander W. van der Laan | s.w.vanderlaan[at]gmail.com | swvanderlaan.github.io.
LS0tCnRpdGxlOiAiUG9sYXJNb3JwaGlzbTogQ0FDIHZzIGNJTVQsIENBRCwgaXNjaGVtaWMgc3Ryb2tlIHN1YnR5cGVzLiIKYXV0aG9yOiAiW1NhbmRlciBXLiB2YW4gZGVyIExhYW4sIFBoRF0oaHR0cHM6Ly9zd3ZhbmRlcmxhYW4uZ2l0aHViLmlvKSB8IEBzd3ZhbmRlcmxhYW4gfCBzLncudmFuZGVybGFhbkBnbWFpbC5jb20iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjYWNoZTogeWVzCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGNvbGxhcHNlOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogICAgZmlnLmFsaWduOiBjZW50ZXIKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGZpZ19oZWlnaHQ6IDYKICAgIGZpZ19yZXRpbmE6IDIKICAgIGZpZ193aWR0aDogNwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgdGhlbWU6IGx1bWVuCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IG5vCiAgICAgIHNtb290aF9zY3JvbGw6IHllcwptYWluZm9udDogQXJpYWwKc3VidGl0bGU6ICJBICdkcnVnZ2FibGUtTUktdGFyZ2V0cycgcHJvamVjdCIKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHtyIGdsb2JhbF9vcHRpb25zLCBpbmNsdWRlID0gRkFMU0V9CiMgZnVydGhlciBkZWZpbmUgc29tZSBrbml0ci1vcHRpb25zLgprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4LCBmaWcucGF0aCA9ICdGaWd1cmVzLycsIAogICAgICAgICAgICAgICAgICAgICAgd3dhcm5pbmcgPSBUUlVFLCAjIHNob3cgd2FybmluZ3MgZHVyaW5nIGNvZGVib29rIGdlbmVyYXRpb24KICBtZXNzYWdlID0gVFJVRSwgIyBzaG93IG1lc3NhZ2VzIGR1cmluZyBjb2RlYm9vayBnZW5lcmF0aW9uCiAgZXJyb3IgPSBUUlVFLCAjIGRvIG5vdCBpbnRlcnJ1cHQgY29kZWJvb2sgZ2VuZXJhdGlvbiBpbiBjYXNlIG9mIGVycm9ycywKICAgICAgICAgICAgICAgICMgdXN1YWxseSBiZXR0ZXIgZm9yIGRlYnVnZ2luZwogIGVjaG8gPSBUUlVFLCAgIyBzaG93IFIgY29kZQogICAgICAgICAgICAgICAgICAgICAgZXZhbCA9IFRSVUUpCmdncGxvdDI6OnRoZW1lX3NldChnZ3Bsb3QyOjp0aGVtZV9taW5pbWFsKCkpCnBhbmRlcjo6cGFuZGVyT3B0aW9ucygidGFibGUuc3BsaXQudGFibGUiLCBJbmYpCmBgYAoKIyBTZXR1cApXZSB3aWxsIGNsZWFuIHRoZSBlbnZpcm9ubWVudCwgc2V0dXAgdGhlIGxvY2F0aW9ucywgZGVmaW5lIGNvbG9ycywgYW5kIGNyZWF0ZSBhIGRhdGVzdGFtcC4KCl9DbGVhbiB0aGUgZW52aXJvbm1lbnQuXwpgYGB7ciBlY2hvID0gRkFMU0V9CnJtKGxpc3QgPSBscygpKQpgYGAKCl9TZXQgbG9jYXRpb25zIGFuZCB3b3JraW5nIGRpcmVjdG9yaWVzLi4uXwpgYGB7ciBMb2NhbFN5c3RlbSwgZWNobyA9IEZBTFNFfQojIyMgT3BlcmF0aW5nIFN5c3RlbSBWZXJzaW9uCiMjIyBNYWNCb29rIFBybwpST09UX2xvYyA9ICIvVXNlcnMvc3d2YW5kZXJsYWFuL09uZURyaXZlIC0gVU1DIFV0cmVjaHQiCiMgU1RPUkFHRV9sb2MgPSAiL1ZvbHVtZXMvTGFDaWUvIgpTVE9SQUdFX2xvYyA9ICIvVXNlcnMvc3d2YW5kZXJsYWFuLyIKCiMjIyBNYWNCb29rIEFpcgojIFJPT1RfbG9jID0gIi9Vc2Vycy9zbGFhbjMvT25lRHJpdmUgLSBVTUMgVXRyZWNodCIKIyBTVE9SQUdFX2xvYyA9ICIvVm9sdW1lcy9MYUNpZS8iCiMgU1RPUkFHRV9sb2MgPSAiL1VzZXJzL3NsYWFuMy8iCgpHRU5PTUlDX2xvYyA9IHBhc3RlMChST09UX2xvYywgIi9HZW5vbWljcyIpCkFFREJfbG9jID0gcGFzdGUwKEdFTk9NSUNfbG9jLCAiL0F0aGVyby1FeHByZXNzL0FFLUFBQV9HU19EQnMiKQpMQUJfbG9jID0gcGFzdGUwKEdFTk9NSUNfbG9jLCAiL0xhYkJ1c2luZXNzIikKClBMSU5LX2xvYz1wYXN0ZTAoU1RPUkFHRV9sb2MsIi9QTElOSyIpCkFFR1NRQ19sb2MgPSAgcGFzdGUwKFBMSU5LX2xvYywgIi9fQUVfT1JJR0lOQUxTL0FFR1NfQ09NQklORURfUUMyMDE4IikKTUlDSElNUF9sb2M9cGFzdGUwKFBMSU5LX2xvYywiL19BRV9PUklHSU5BTFMvQUVHU19DT01CSU5FRF9FQUdMRTJfMTAwMEdwM3Y1SFJDcjExIikKCkdXQVNfbG9jPXBhc3RlMChQTElOS19sb2MsIi9fR1dBU19EYXRhc2V0cyIpCgpQUk9KRUNUX2xvYyA9IHBhc3RlMChQTElOS19sb2MsICIvYW5hbHlzZXMvY29uc29ydGlhL0NIQVJHRV8xMDAwR19DQUMiKQoKIyB1c2UgdGhpcyBpZiB0aGVyZSBpcyByZWxldmFudCBpbmZvcm1hdGlvbiBoZXJlLgpUQVJHRVRfbG9jID0gcGFzdGUwKFBST0pFQ1RfbG9jLCAiL3RhcmdldHMiKQoKIyMjIFNPTUUgVkFSSUFCTEVTIFdFIE5FRUQgRE9XTiBUSEUgTElORQpUUkFJVF9PRl9JTlRFUkVTVCA9ICJDQUMiICMgUGhlbm90eXBlClBST0pFQ1ROQU1FID0gIkNBQyIKCmNhdCgiXG5DcmVhdGUgYSBuZXcgYW5hbHlzaXMgZGlyZWN0b3J5Li4uXG4iKQppZmVsc2UoIWRpci5leGlzdHMoZmlsZS5wYXRoKFBST0pFQ1RfbG9jLCAiLyIsUFJPSkVDVE5BTUUpKSwgCiAgICAgICBkaXIuY3JlYXRlKGZpbGUucGF0aChQUk9KRUNUX2xvYywgIi8iLFBST0pFQ1ROQU1FKSksIAogICAgICAgRkFMU0UpCkFOQUxZU0lTX2xvYyA9IHBhc3RlMChQUk9KRUNUX2xvYywiLyIsUFJPSkVDVE5BTUUpCgppZmVsc2UoIWRpci5leGlzdHMoZmlsZS5wYXRoKEFOQUxZU0lTX2xvYywgIi9QTE9UUyIpKSwgCiAgICAgICBkaXIuY3JlYXRlKGZpbGUucGF0aChBTkFMWVNJU19sb2MsICIvUExPVFMiKSksIAogICAgICAgRkFMU0UpClBMT1RfbG9jID0gcGFzdGUwKEFOQUxZU0lTX2xvYywiL1BMT1RTIikKCmlmZWxzZSghZGlyLmV4aXN0cyhmaWxlLnBhdGgoUExPVF9sb2MsICIvUUMiKSksIAogICAgICAgZGlyLmNyZWF0ZShmaWxlLnBhdGgoUExPVF9sb2MsICIvUUMiKSksIAogICAgICAgRkFMU0UpClFDX2xvYyA9IHBhc3RlMChQTE9UX2xvYywiL1FDIikKCmlmZWxzZSghZGlyLmV4aXN0cyhmaWxlLnBhdGgoQU5BTFlTSVNfbG9jLCAiL09VVFBVVCIpKSwgCiAgICAgICBkaXIuY3JlYXRlKGZpbGUucGF0aChBTkFMWVNJU19sb2MsICIvT1VUUFVUIikpLCAKICAgICAgIEZBTFNFKQpPVVRfbG9jID0gcGFzdGUwKEFOQUxZU0lTX2xvYywgIi9PVVRQVVQiKQoKaWZlbHNlKCFkaXIuZXhpc3RzKGZpbGUucGF0aChBTkFMWVNJU19sb2MsICIvQkFTRUxJTkUiKSksIAogICAgICAgZGlyLmNyZWF0ZShmaWxlLnBhdGgoQU5BTFlTSVNfbG9jLCAiL0JBU0VMSU5FIikpLCAKICAgICAgIEZBTFNFKQpCQVNFTElORV9sb2MgPSBwYXN0ZTAoQU5BTFlTSVNfbG9jLCAiL0JBU0VMSU5FIikKCmlmZWxzZSghZGlyLmV4aXN0cyhmaWxlLnBhdGgoUFJPSkVDVF9sb2MsICIvU05QIikpLCAKICAgICAgIGRpci5jcmVhdGUoZmlsZS5wYXRoKFBST0pFQ1RfbG9jLCAiL1NOUCIpKSwgCiAgICAgICBGQUxTRSkKU05QX2xvYyA9IHBhc3RlMChQUk9KRUNUX2xvYywgIi9TTlAiKQoKc2V0d2QocGFzdGUwKFBST0pFQ1RfbG9jKSkKZ2V0d2QoKQpsaXN0LmZpbGVzKCkKCmBgYAoKXy4uLiBhIHBhY2thZ2UtaW5zdGFsbGF0aW9uIGZ1bmN0aW9uIC4uLl8KYGBge3J9CnNvdXJjZShwYXN0ZTAoUFJPSkVDVF9sb2MsICIvc2NyaXB0cy9mdW5jdGlvbnMuUiIpKQpgYGAKCgpfLi4uIGFuZCBsb2FkIHRob3NlIHBhY2thZ2VzLl8KYGBge3IgbG9hZGluZ19wYWNrYWdlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcy5hdXRvKCJyZWFkciIpCmluc3RhbGwucGFja2FnZXMuYXV0bygib3B0cGFyc2UiKQppbnN0YWxsLnBhY2thZ2VzLmF1dG8oInRvb2xzIikKaW5zdGFsbC5wYWNrYWdlcy5hdXRvKCJkcGx5ciIpCmluc3RhbGwucGFja2FnZXMuYXV0bygidGlkeXIiKQppbnN0YWxsLnBhY2thZ2VzLmF1dG8oIm5hbmlhciIpCgojIFRvIGdldCAnZGF0YS50YWJsZScgd2l0aCAnZndyaXRlJyB0byBiZSBhYmxlIHRvIGRpcmVjdGx5IHdyaXRlIGd6aXBwZWQtZmlsZXMKIyBSZWY6IGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQyNzg4NDAxL2lzLXBvc3NpYmxlLXRvLXVzZS1md3JpdGUtZnJvbS1kYXRhLXRhYmxlLXdpdGgtZ3pmaWxlCiMgaW5zdGFsbC5wYWNrYWdlcygiZGF0YS50YWJsZSIsIHJlcG9zID0gImh0dHBzOi8vUmRhdGF0YWJsZS5naXRsYWIuaW8vZGF0YS50YWJsZSIpCmxpYnJhcnkoZGF0YS50YWJsZSkKaW5zdGFsbC5wYWNrYWdlcy5hdXRvKCJ0aWR5dmVyc2UiKQppbnN0YWxsLnBhY2thZ2VzLmF1dG8oIkRUIikKCmluc3RhbGwucGFja2FnZXMuYXV0bygia25pdHIiKQppbnN0YWxsLnBhY2thZ2VzLmF1dG8oImVlcHRvb2xzIikKCmluc3RhbGwucGFja2FnZXMuYXV0bygiaGF2ZW4iKQppbnN0YWxsLnBhY2thZ2VzLmF1dG8oInRhYmxlb25lIikKCmluc3RhbGwucGFja2FnZXMuYXV0bygiQmxhbmRBbHRtYW5MZWgiKQoKIyBJbnN0YWxsIHRoZSBkZXZ0b29scyBwYWNrYWdlIGZyb20gSGFkbGV5IFdpY2toYW0KaW5zdGFsbC5wYWNrYWdlcy5hdXRvKCdkZXZ0b29scycpCmxpYnJhcnkoZGV2dG9vbHMpIAoKIyBmb3IgcGxvdHRpbmcKaW5zdGFsbC5wYWNrYWdlcy5hdXRvKCJwaGVhdG1hcCIpCmluc3RhbGwucGFja2FnZXMuYXV0bygiZm9yZXN0cGxvdCIpCmluc3RhbGwucGFja2FnZXMuYXV0bygiZ2dwbG90MiIpCmluc3RhbGwucGFja2FnZXMuYXV0bygiZ2dwdWJyIikKaW5zdGFsbC5wYWNrYWdlcy5hdXRvKCJnZ3JlcGVsIikKCmluc3RhbGwucGFja2FnZXMuYXV0bygiVXBTZXRSIikKCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigidGhvbWFzcDg1L3BhdGNod29yayIpCgojIEZvciByZWdpb25hbCBhc3NvY2lhdGlvbiBwbG90cwppbnN0YWxsX2dpdGh1Yigib2xpdmlhc2FiaWsvUkFDRVIiKSAKCiMgSW5zdGFsbCBnZ3JlcGVsIHBhY2thZ2UgaWYgbmVlZGVkCiMgaW5zdGFsbC5wYWNrYWdlcy5hdXRvKGdncmVwZWwpCmxpYnJhcnkoZ2dyZXBlbCkKCmluc3RhbGwucGFja2FnZXMuYXV0byhxdmFsdWUpCgojIGZvciBwcnVuaW5nIG9mIHNpZ25pZmljYW50IG92ZXJsYXBwaW5nIFNOUHMKbGlicmFyeSgicmVtb3RlcyIpCnJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJNUkNJRVUvaWV1Z3dhc3IiKQoKIyBmb3IgYW5ub3RhdGlvbgppbnN0YWxsLnBhY2thZ2VzLmF1dG8oImJpb21hUnQiKQoKYGBgCgpfV2Ugd2lsbCBjcmVhdGUgYSBkYXRlc3RhbXAgYW5kIGRlZmluZSB0aGUgVXRyZWNodCBTY2llbmNlIFBhcmsgQ29sb3VyIFNjaGVtZV8uCmBgYHtyIFNldHRpbmc6IENvbG9yc30KClRvZGF5ID0gZm9ybWF0KGFzLkRhdGUoYXMuUE9TSVhsdChTeXMudGltZSgpKSksICIlWSVtJWQiKQpUb2RheS5SZXBvcnQgPSBmb3JtYXQoYXMuRGF0ZShhcy5QT1NJWGx0KFN5cy50aW1lKCkpKSwgIiVBLCAlQiAlZCwgJVkiKQoKIyMjIFV0cmVjaHRTY2llbmNlUGFya0NvbG91cnNTY2hlbWUKIyMjCiMjIyBXZWJzaXRldG9jb252ZXJ0SEVYdG9SR0I6aHR0cDovL2hleC5jb2xvcnJycy5jb20uCiMjIyBGb3Jzb21lZnVuY3Rpb25zeW91c2hvdWxkZGl2aWRldGhlc2VudW1iZXJzYnkyNTUuCiMjIwojIyMJTm8uCUNvbG9yCQkJICAgICAgSEVYCShSR0IpCQkJCQkJICAgICAgICAgICAgICBDSFIJCSAgTUFGL0lORk8KIyMjLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMjIwkxCSAgeWVsbG93CQkJICAgICNGQkI4MjAgKDI1MSwxODQsMzIpCQkJCSAgICAgID0+CTEJCW9yIDEuMD5JTkZPCiMjIwkyCSAgZ29sZAkJCSAgICAgICNGNTlEMTAgKDI0NSwxNTcsMTYpCQkJCSAgICAgID0+CTIJCQojIyMJMwkgIHNhbG1vbgkJCSAgICAjRTU1NzM4ICgyMjksODcsNTYpCQkJCSAgICAgID0+CTMJCW9yIDAuMDU8TUFGPDAuMiBvciAwLjQ8SU5GTzwwLjYKIyMjCTQJICBkYXJrcGluawkJICAgICNEQjAwM0YgKCgyMTksMCw2MykJCQkJICAgICAgPT4JNAkJCiMjIwk1CSAgbGlnaHRwaW5rCQkgICAgI0UzNTQ5MyAoMjI3LDg0LDE0NykJCQkJICAgICAgPT4JNQkJb3IgMC44PElORk88MS4wCiMjIwk2CSAgcGluawkJCSAgICAgICNENTI2N0IgKDIxMywzOCwxMjMpCQkJCSAgICAgID0+CTYJCQojIyMJNwkgIGhhcmRwaW5rCQkgICAgI0NDMDA3MSAoMjA0LDAsMTEzKQkJCQkgICAgICA9Pgk3CQkKIyMjCTgJICBsaWdodHB1cnBsZQkgICAgI0E4NDQ4QSAoMTY4LDY4LDEzOCkJCQkJICAgICAgPT4JOAkJCiMjIwk5CSAgcHVycGxlCQkJICAgICM5QTM0ODAgKDE1NCw1MiwxMjgpCQkJCSAgICAgID0+CTkJCQojIyMJMTAJbGF2ZW5kZWwJCSAgICAjOEQ1QjlBICgxNDEsOTEsMTU0KQkJCQkgICAgICA9PgkxMAkJCiMjIwkxMQlibHVlcHVycGxlCQkgICM3MDUyOTYgKDExMiw4MiwxNTApCQkJCSAgICAgID0+CTExCQkKIyMjCTEyCXB1cnBsZWJsdWUJCSAgIzY4NkFBOSAoMTA0LDEwNiwxNjkpCQkJICAgICAgPT4JMTIJCQojIyMJMTMJbGlnaHRwdXJwbGVibHVlCSM2MTczQUQgKDk3LDExNSwxNzMvMTAxLDEyMCwxODApCT0+CTEzCQkKIyMjCTE0CXNlYWJsdWUJCQkgICAgIzRDODFCRiAoNzYsMTI5LDE5MSkJCQkJICAgICAgPT4JMTQJCQojIyMJMTUJc2t5Ymx1ZQkJCSAgICAjMkY4QkM5ICg0NywxMzksMjAxKQkJCQkgICAgICA9PgkxNQkJCiMjIwkxNglhenVyYmx1ZQkJICAgICMxMjkwRDkgKDE4LDE0NCwyMTcpCQkJCSAgICAgID0+CTE2CQlvciAwLjAxPE1BRjwwLjA1IG9yIDAuMjxJTkZPPDAuNAojIyMJMTcJbGlnaHRhenVyYmx1ZQkgICMxMzk2RDggKDE5LDE1MCwyMTYpCQkJCSAgICAgID0+CTE3CQkKIyMjCTE4CWdyZWVuYmx1ZQkJICAgICMxNUE2QzEgKDIxLDE2NiwxOTMpCQkJCSAgICAgID0+CTE4CQkKIyMjCTE5CXNlYXdlZWRncmVlbgkgICM1RUIxN0YgKDk0LDE3NywxMjcpCQkJCSAgICAgID0+CTE5CQkKIyMjCTIwCXllbGxvd2dyZWVuCQkgICM4NkI4MzMgKDEzNCwxODQsNTEpCQkJCSAgICAgID0+CTIwCQkKIyMjCTIxCWxpZ2h0bW9zc2dyZWVuCSNDNUQyMjAgKDE5NywyMTAsMzIpCQkJCSAgICAgID0+CTIxCQkKIyMjCTIyCW1vc3NncmVlbgkJICAgICM5RkMyMjggKDE1OSwxOTQsNDApCQkJCSAgICAgID0+CTIyCQlvciBNQUY+MC4yMCBvciAwLjY8SU5GTzwwLjgKIyMjCTIzCWxpZ2h0Z3JlZW4JICAJIzc4QjExMyAoMTIwLDE3NywxOSkJCQkJICAgICAgPT4JMjMvWAojIyMJMjQJZ3JlZW4JCQkgICAgICAjNDlBMDFEICg3MywxNjAsMjkpCQkJCSAgICAgID0+CTI0L1kKIyMjCTI1CWdyZXkJCQkgICAgICAjNTk1QTVDICg4OSw5MCw5MikJCQkJICAgICAgICA9PgkyNS9YWQlvciBNQUY8MC4wMSBvciAwLjA8SU5GTzwwLjIKIyMjCTI2CWxpZ2h0Z3JleQkJICAgICNBMkEzQTQJKDE2MiwxNjMsMTY0KQkJCSAgICAgID0+CTI2L01UCiMjIwojIyMJQURESVRJT05BTCBDT0xPUlMKIyMjCTI3CW1pZGdyZXkJCQkjRDdEOEQ3CiMjIwkyOAl2ZXJ5bGlnaHRncmV5CSNFQ0VDRUMiCiMjIwkyOQl3aGl0ZQkJCSNGRkZGRkYKIyMjCTMwCWJsYWNrCQkJIzAwMDAwMAojIyMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgp1aXRob2ZfY29sb3IgPSBjKCIjRkJCODIwIiwiI0Y1OUQxMCIsIiNFNTU3MzgiLCIjREIwMDNGIiwiI0UzNTQ5MyIsIiNENTI2N0IiLAogICAgICAgICAgICAgICAgICIjQ0MwMDcxIiwiI0E4NDQ4QSIsIiM5QTM0ODAiLCIjOEQ1QjlBIiwiIzcwNTI5NiIsIiM2ODZBQTkiLAogICAgICAgICAgICAgICAgICIjNjE3M0FEIiwiIzRDODFCRiIsIiMyRjhCQzkiLCIjMTI5MEQ5IiwiIzEzOTZEOCIsIiMxNUE2QzEiLAogICAgICAgICAgICAgICAgICIjNUVCMTdGIiwiIzg2QjgzMyIsIiNDNUQyMjAiLCIjOUZDMjI4IiwiIzc4QjExMyIsIiM0OUEwMUQiLAogICAgICAgICAgICAgICAgICIjNTk1QTVDIiwiI0EyQTNBNCIsICIjRDdEOEQ3IiwgIiNFQ0VDRUMiLCAiI0ZGRkZGRiIsICIjMDAwMDAwIikKCnVpdGhvZl9jb2xvcl9sZWdlbmQgPSBjKCIjRkJCODIwIiwgIiNGNTlEMTAiLCAiI0U1NTczOCIsICIjREIwMDNGIiwgIiNFMzU0OTMiLAogICAgICAgICAgICAgICAgICAgICAgICAiI0Q1MjY3QiIsICIjQ0MwMDcxIiwgIiNBODQ0OEEiLCAiIzlBMzQ4MCIsICIjOEQ1QjlBIiwKICAgICAgICAgICAgICAgICAgICAgICAgIiM3MDUyOTYiLCAiIzY4NkFBOSIsICIjNjE3M0FEIiwgIiM0QzgxQkYiLCAiIzJGOEJDOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICIjMTI5MEQ5IiwgIiMxMzk2RDgiLCAiIzE1QTZDMSIsICIjNUVCMTdGIiwgIiM4NkI4MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAiI0M1RDIyMCIsICIjOUZDMjI4IiwgIiM3OEIxMTMiLCAiIzQ5QTAxRCIsICIjNTk1QTVDIiwKICAgICAgICAgICAgICAgICAgICAgICAgIiNBMkEzQTQiLCAiI0Q3RDhENyIsICIjRUNFQ0VDIiwgIiNGRkZGRkYiLCAiIzAwMDAwMCIpCiMjIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYAoKIyBJbnRyb2R1Y3Rpb24KCldlIHdpbGwgYXBwbHkgUG9sYXJNb3JwaGlzbSB0byBjb21wYXJlIHRoZSBDQUMgR1dBUyB2cyBjSU1ULCBDQUQsIGFuZCBpc2NoZW1pYyBzdHJva2Ugc3VidHlwZXMuCgpgYGB7cn0KCmxpYnJhcnkoZGV2dG9vbHMpCmluc3RhbGxfZ2l0aHViKCJVTUNVZ2VuZXRpY3MvUG9sYXJNb3JwaGlzbSIpCmxpYnJhcnkoUG9sYXJNb3JwaGlzbSkKCmBgYAoKCiMgTG9hZGluZyBhbmQgcGFyc2luZyBkYXRhCgpXZSBuZWVkIHRvIGxvYWQgYW5kIHBhcnNlIHRoZSBkYXRhIGZpcnN0LgoKV2UgdXNlZCBbYGd3YXMyY29qb2BdKGh0dHBzOi8vZ2l0aHViLmNvbS9DaXJjdWxhdG9yeUhlYWx0aC9nd2FzMmNvam8pIHRvIHBhcnNlIHRoZSBkYXRhc2V0cyB3aXRoIDEwMDBHIHBoYXNlIDMgYXMgYSByZWZlcmVuY2UuIFRoaXMgcmVzdWx0ZWQgaW4gY2xlYW5lZCBhbmQgaGFybW9uaXplZCBHV0FTIGRhdGFzZXRzIHdpdGggc3RhbmRhcmRpemVkIGhlYWRpbmdzLCBhbmQgY29udGFpbmluZyByc0lEcyB0aGF0IGFyZSBhbHNvIHByZXNlbnQgaW4gMTAwMEcuIEZvciBzb21lIEdXQVMgc3R1ZGllcywgbWlzc2luZyBiZXRhIG9yIHNlIHdlcmUgcmVjb3ZlcmVkIGZyb20gei1zY29yZXMgb3IKbWlzc2luZyBjaHI6YnAgcG9zaXRpb25zL2ZyZXF1ZW5jaWVzIGZyb20gcnNpZHMgdXNpbmcgdGhlIGRiU05QMTUzIENPTU1PTiBkYXRhYmFzZS4gUHJpb3IgdG8gY29udmVyc2lvbiwgbm9uLWJpYWxsZWxpYyBTTlBzIHdlcmUgcmVtb3ZlZCBmcm9tIHRoZSBnZW5vbWljIHJlZmVyZW5jZS4gVGhlcmVmb3JlLCBzb21lIHRyaWFsbGVsaWMgU05QcyBtYXkgbm90IGJlIHByZXNlbnQuCgpgYGAKUGhlbm90eXBlICAgICAgICAgICAgICAgICAgICAgICBTaG9ydCAgICAgICBTb3VyY2UgRmlsZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUE1JRApDb3JvbmFyeSBjYWxjaWZpY2F0aW9uICAgICAgICAgIENBQyAgICAgICAgIApDb3JvbmFyeSBhcnRlcnkgZGlzZWFzZSAgICAgICAgIENBRCAgICAgICAgIFVLQkIuR1dBUzFLRy5FWE9NRS5DQUQuU09GVC5NRVRBLlB1YmxpY1JlbGVhc2UuMzAwNTE3LnR4dC5neiAgICAgICAgICAgICAgICAyODcxNDk3NQoKQW55IHN0cm9rZSAgICAgICAgICAgICAgICAgICAgICBBUyAgICAgICAgICBNRUdBU1RST0tFLjEuQVMuRVVSLkdDX2ZpbHRlcmVkX1hfbm9jYXNlc19oZXQudHh0Lmd6ICAgICAgICAgICAgICAgICAgICAgICAgMjk1MzEzNTQKQW55IGlzY2hlbWljIHN0cm9rZSAgICAgICAgICAgICBJUyAgICAgICAgICBNRUdBU1RST0tFLjIuSVMuRVVSLkdDX2ZpbHRlcmVkX1hfbm9jYXNlc19oZXQudHh0Lmd6ICAgICAgICAgICAgICAgICAgICAgICAgMjk1MzEzNTQKTGFyZ2UgYXJ0ZXJ5IHN0cm9rZSAgICAgICAgICAgICBMQVMgICAgICAgICBNRUdBU1RST0tFLjMuTEFTLkVVUi5HQ19maWx0ZXJlZF9YX25vY2FzZXNfaGV0LnR4dC5neiAgICAgICAgICAgICAgICAgICAgICAgMjk1MzEzNTQKQ2FyZGlvLWVtYm9saWMgc3Ryb2tlICAgICAgICAgICBDRVMgICAgICAgICBNRUdBU1RST0tFLjQuQ0UuRVVSLkdDX2ZpbHRlcmVkX1hfbm9jYXNlc19oZXQudHh0Lmd6ICAgICAgICAgICAgICAgICAgICAgICAgMjk1MzEzNTQKU21hbGwgdmVzc2VsIGRpc2Vhc2UgICAgICAgICAgICBTVkQgICAgICAgICBNRUdBU1RST0tFLjUuU1ZELkVVUi5HQ19maWx0ZXJlZF9YX25vY2FzZXNfaGV0LnR4dC5neiAgICAgICAgICAgICAgICAgICAgICAgMjk1MzEzNTQKCkNhcm90aWQgSU1UICAgICAgICAgICAgICAgICAgICAgY0lNVCAgICAgICAgSU1ULkVBLk1FVEEuTUFGMS5IZXRERjRfanVuLmNzdi5neiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDMwNTg2NzIyClBsYXF1ZSBwcmVzZW5jZSAgICAgICAgICAgICAgICAgUGxhcXVlICAgICAgUGxhcXVlX21ldGFfMDMyMjE4LmNzdi5neiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDMwNTg2NzIyCgpgYGAKCiMjIEdXQVMgZGF0YXNldHMKCmBgYHtyfQpDT0pPX2xvYyA9IHBhc3RlMChHV0FTX2xvYywgIi9fY29qby9yc2lkIikKCiMgdHJhaXRzIDwtIGMoIkNBRCIsICJBUyIsICJJUyIsICJMQVMiLCAiQ0VTIiwgIlNWRCIsICJjSU1UIiwgIlBsYXF1ZSIpCnRyYWl0cyA8LSBjKCJDQUQiKQoKZm9yKHRyYWl0IGluIHRyYWl0cyl7CiAgY2F0KHBhc3RlMCgiXG5Qcm9jZXNzaW5nIGRhdGEgZm9yIFsiLCB0cmFpdCwgIl0gKCIscGFzdGUwKENPSk9fbG9jLCAiLyIsIHRyYWl0LCAiLmNvam8uZ3oiKSwiKS5cbiIpKQogIAogIHRibCA8LSBhc190aWJibGUoZnJlYWQocGFzdGUwKENPSk9fbG9jLCAiLyIsIHRyYWl0LCAiLmNvam8uZ3oiKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dQcm9ncmVzcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICBuVGhyZWFkID0gNCkpCiAgIyB0aGUgY29sdW1uIG5hbWVzIGFyZSAiU05QIiAgIkExIiAgICJBMiIgICAiZnJlcSIgImIiICAgICJzZSIgICAicCIgICAgIm4iCiAgIyB3ZSBoYXZlIHRvIGNoYW5nZSB0aGVtIHNvIFBvbGFyTW9ycGhpc20ga25vd3Mgd2hhdCBlYWNoIGNvbHVtbiBjb250YWlucwogIGNvbG5hbWVzKHRibCkgPC0gYygic25waWQiLCJhMSIsImEyIiwiZnJlcSIsImJldGEiLCJzZSIsInB2YWwiLCAibiIpICMgbm90ZSB0aGF0IFBvbGFyTW9ycGhpc20gZG9lcyBub3QgbmVlZCBvciB1c2UgdGhlICJuIiBjb2x1bW4KICBhc3NpZ24odHJhaXQsIHRibCkKCn0Kcm0odGJsKQpgYGAKCgojIyBDb3JvbmFyeSBhcnRlcnkgY2FsY2lmaWNhdGlvbgpgYGB7cn0KY2F0KCJcbkdXQVM6IGNvcm9uYXJ5IGFydGVyeSBjYWxjaWZpY2F0aW9uLCBDQUM6XG4iKQpnd2FzX3N1bXN0YXRzIDwtIHJlYWRSRFMoZmlsZSA9IHBhc3RlMChPVVRfbG9jLCAiL2d3YXNfc3Vtc3RhdHNfY29tcGxldGUucmRzIikpCmhlYWQoZ3dhc19zdW1zdGF0cykKbnJvdyhnd2FzX3N1bXN0YXRzKQoKYGBgCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCgpDQUN0aWIgPC0gYXNfdGliYmxlKGd3YXNfc3Vtc3RhdHMgJT4lIAogICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KHJzSUQsIENociwgUG9zaXRpb24sIEFsbGVsZTEsIEFsbGVsZTIsIEZyZXExLCBFZmZlY3QsIFNFLCBQdmFsdWUsIE4pICU+JSAjIGVuZCBvZiBzZWxlY3QKICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShBbGxlbGUxID0gdG91cHBlcihBbGxlbGUxKSkgJT4lICMgY2hhbmdlIGFsbCBjaGFyYWN0ZXJzIHRvIHVwcGVyY2FzZQogICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKEFsbGVsZTIgPSB0b3VwcGVyKEFsbGVsZTIpKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICByZW5hbWUoICMgcmVuYW1lIHZhcmlhYmxlcwogICAgICAgICAgICAgICAgICAgICAgQlAgPSBQb3NpdGlvbiwgIyBuZXcgdnMgb2xkCiAgICAgICAgICAgICAgICAgICAgICBzbnBpZCA9IHJzSUQsCiAgICAgICAgICAgICAgICAgICAgICBhMSA9IEFsbGVsZTEsIAogICAgICAgICAgICAgICAgICAgICAgYTIgPSBBbGxlbGUyLCAKICAgICAgICAgICAgICAgICAgICAgIGZyZXEgPSBGcmVxMSwgCiAgICAgICAgICAgICAgICAgICAgICBiZXRhID0gRWZmZWN0LAogICAgICAgICAgICAgICAgICAgICAgc2UgPSBTRSwKICAgICAgICAgICAgICAgICAgICAgIHB2YWwgPSBQdmFsdWUsCiAgICAgICAgICAgICAgICAgICAgICBuID0gTgogICAgICAgICAgICAgICAgICAgICAgKSAlPiUgIyBlbmQgb2YgcmVuYW1lCiAgICAgICAgICAgICAgICAgICAgICByZWxvY2F0ZShzbnBpZCwgQ2hyLCBCUCwgYTEsIGEyLCBmcmVxLCBiZXRhLCBzZSwgcHZhbCwgbikgIyBzZXQgdGhlIG9yZGVyIG9mIGNvbHVtbnMKICAgICAgICAgICAgICAgICAgICApCmhlYWQoQ0FDdGliKQpzdHIoQ0FDdGliKQpucm93KENBQ3RpYikKcm0oZ3dhc19zdW1zdGF0cykKYGBgCgoKCgoKIyMgU2F2aW5nCgpMZXQncyBzYXZlIHRoaXMgd29yayBmb3IgZWFzeSBhY2Nlc3MuCgpgYGB7cn0KCnNhdmVSRFMoQ0FDdGliLCBmaWxlID0gcGFzdGUwKE9VVF9sb2MsICIvIiwgVG9kYXksICIuQ0FDLnJkcyIpKQpzYXZlUkRTKENBRCwgZmlsZSA9IHBhc3RlMChPVVRfbG9jLCAiLyIsIFRvZGF5LCAiLkNBRC5yZHMiKSkKCiMgcm0oQ0FDdGliLCBDQUQpCiMgCiMgc2F2ZVJEUyhDSU1ULCBmaWxlID0gcGFzdGUwKE9VVF9sb2MsICIvIiwgVG9kYXksICIuQ0lNVC5yZHMiKSkKIyBzYXZlUkRTKFBMQVEsIGZpbGUgPSBwYXN0ZTAoT1VUX2xvYywgIi8iLCBUb2RheSwgIi5QTEFRLnJkcyIpKQojIAojIHJtKENJTVQsIFBMQVEpCiMgCiMgc2F2ZVJEUyhBUywgZmlsZSA9IHBhc3RlMChPVVRfbG9jLCAiLyIsIFRvZGF5LCAiLkFTLnJkcyIpKQojIHNhdmVSRFMoSVMsIGZpbGUgPSBwYXN0ZTAoT1VUX2xvYywgIi8iLCBUb2RheSwgIi5JUy5yZHMiKSkKIyBzYXZlUkRTKExBUywgZmlsZSA9IHBhc3RlMChPVVRfbG9jLCAiLyIsIFRvZGF5LCAiLkxBUy5yZHMiKSkKIyBzYXZlUkRTKENFLCBmaWxlID0gcGFzdGUwKE9VVF9sb2MsICIvIiwgVG9kYXksICIuQ0UucmRzIikpCiMgc2F2ZVJEUyhTVkQsIGZpbGUgPSBwYXN0ZTAoT1VUX2xvYywgIi8iLCBUb2RheSwgIi5TVkQucmRzIikpCiMgCiMgcm0oQVMsIElTLCBMQVMsIENFLCBTVkQpCgpgYGAKCiMjIExvYWRpbmcgZm9yIGVhc3kgYWNjZXNzCgpgYGB7cn0KCkNBQ3RpYiA8LSByZWFkUkRTKGZpbGUgPSBwYXN0ZTAoT1VUX2xvYywgIi8yMDIyMDEzMS5DQUMucmRzIikpCkNBRHRpYiA8LSByZWFkUkRTKGZpbGUgPSBwYXN0ZTAoT1VUX2xvYywgIi8yMDIyMDEzMS5DQUQucmRzIikpCgojIENJTVR0aWIgPC0gcmVhZFJEUyhmaWxlID0gcGFzdGUwKE9VVF9sb2MsICIvMjAyMjAxMzEuQ0lNVC5yZHMiKSkKIyBQTEFRdGliIDwtIHJlYWRSRFMoZmlsZSA9IHBhc3RlMChPVVRfbG9jLCAiLzIwMjIwMTMxLlBMQVEucmRzIikpCiMgCiMgQVN0aWIgPC0gcmVhZFJEUyhmaWxlID0gcGFzdGUwKE9VVF9sb2MsICIvMjAyMjAxMzEuQVMucmRzIikpCiMgSVN0aWIgPC0gcmVhZFJEUyhmaWxlID0gcGFzdGUwKE9VVF9sb2MsICIvMjAyMjAxMzEuSVMucmRzIikpCiMgTEFTdGliIDwtIHJlYWRSRFMoZmlsZSA9IHBhc3RlMChPVVRfbG9jLCAiLzIwMjIwMTMxLkxBUy5yZHMiKSkKIyBDRXRpYiA8LSByZWFkUkRTKGZpbGUgPSBwYXN0ZTAoT1VUX2xvYywgIi8yMDIyMDEzMS5DRS5yZHMiKSkKIyBTVkR0aWIgPC0gcmVhZFJEUyhmaWxlID0gcGFzdGUwKE9VVF9sb2MsICIvMjAyMjAxMzEuU1ZELnJkcyIpKQoKYGBgCgoKCgojIFBvbGFyTW9ycGhpemUKCldlIG5lZWQgdG8gY2hvb3NlIG9uZSBvZiB0aGUgR1dBUyBhcyByZWZlcmVuY2UgdG8gbWFrZSBzdXJlIGFsbCBHV0FTcyBoYXZlIHRoZSBzYW1lIHJlZmVyZW5jZSBhbmQgYWx0ZXJuYXRpdmUgYWxsZWxlIGZvciBlYWNoIFNOUC4gV2Ugd2lsbCBtYWtlIGByIFRSQUlUX09GX0lOVEVSRVNUYCB0aGUgcmVmZXJlbmNlLCBhbmQgJ2ZsaXAnIHRoZSBhbGxlbGVzIG9mIGFsbCB0aGUgR1dBU3Mgc28gdGhleSBhbGlnbiB3aXRoIGByIFRSQUlUX09GX0lOVEVSRVNUYC4KCiMjIENsZWFuIHVwIENBQyBHV0FTCgpUaGVyZSBhcmUgc29tZSB2YXJpYW50cyB3aXRob3V0ICdyc0lEJywgd2UgYXJlIGdvaW5nIHRvIHJlbW92ZSB0aGVzZS4KCmBgYHtyfQoKQ0FDdGliZiA8LSBDQUN0aWIgJT4lIAogIGZpbHRlcihzbnBpZCE9Jy4nKSAlPiUgIyBlbmQgb2YgcmVuYW1lCiAgICAgICAgICAgICAgICAgICAgICByZWxvY2F0ZShzbnBpZCwgYmV0YSwgc2UsIGExLCBhMiwgZnJlcSwgcHZhbCwgQ2hyLCBCUCwgbikgIyBzZXQgdGhlIG9yZGVyIG9mIGNvbHVtbnMKcm0oQ0FDdGliKQpgYGAKCldlIHdpbGwgbWFrZSB0aGUgbGlzdCBvZiBTTlBzIGZvciBhbGlnbm1lbnQuCmBgYHtyfQp2YXJpYW50c19vZl9pbnRlcmVzdCA8LSBDQUN0aWJmJHNucGlkW2dyZXBsKCJycyIsIENBQ3RpYmYkc25waWQpXQoKYGBgCgoKQmVjYXVzZSB0aGUgZnVuY3Rpb24gYEFsbGVsZWZsaXBgIG5vdCBvbmx5IGZsaXBzIHRoZSBhbGxlbGVzLCBidXQgYWxzbyBhZGRzIGEgei1zY29yZSBjb2x1bW4sIHdlIGhhdmUgdG8gbWFudWFsbHkgZG8gdGhhdCBmb3IgYHIgVFJBSVRfT0ZfSU5URVJFU1RgLgoKYGBge3J9CgpDQUN0aWJmJHogPC0gQ0FDdGliZiRiZXRhL0NBQ3RpYmYkc2UKCmBgYAoKIyMgQWxpZ25pbmcgQWxsZWxlcwoKYGBge3J9CkNBRHRpYiA8LSBQb2xhck1vcnBoaXNtOjpBbGxlbGVGbGlwKHN1bXN0YXRzID0gQ0FELCBzbnBzID0gQ0FDdGliZiAlPiUgc2VsZWN0KHNucGlkLCBhMSwgYTIpLCBzbnBpZCA9ICJzbnBpZCIsIG9ubHkuYTIgPSBGKQpybShDQUQpCmBgYAoKIyMgQ29udmVydGluZyB0byBwb2xhcgpOb3cgd2UgYXJlIGNvbnZlcnRpbmcgR1dBUyB0byBwb2xhci1jb29yZGluYXRlcy4KYGBge3J9CgpDQUNfQ0FEdGliZiA8LSBQb2xhck1vcnBoaXNtOjpDb252ZXJ0VG9Qb2xhcihkZm5hbWVzID0gYygiQ0FDdGliZiIsICJDQUR0aWIiKSwgIyBub3QgY2xlYXIgaG93IHRoaXMgc2hvdWxkIGJlIGdpdmVuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNucGlkID0gInNucGlkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hpdGVuID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY292c25wcyA9IHZhcmlhbnRzX29mX2ludGVyZXN0LCAjIGlzIHRoaXMgcmVxdWlyZWQ/CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1haGFsYW5vYmlzLnRocmVzaG9sZCA9IDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoaXRlbmluZy5tZXRob2QgPSAiWkNBLWNvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExEY29ycmVjdCA9IEZBTFNFLCAjIHdlIGRvbid0IG5lZWQgdGhhdCBhcyB3ZSBkZS1jb3JyZWxhdGUgdGhyb3VnaCB3aGl0ZW5pbmcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGQucGF0aCA9IHBhc3RlMChQUk9KRUNUX2xvYywiL2V1cl93X2xkX2Noci8iKSkgCgpoZWFkKENBQ19DQUR0aWJmKQpzdHIoQ0FDX0NBRHRpYmYpCgpgYGAKCiMjIyBQb2xhciBwbG90dGluZwpgYGB7cn0KdGVtcCA8LSBDQUNfQ0FEdGliZiAlPiUKICBmaWx0ZXIociA+IDQpCgojIHNldCB0aGUgbGltaXRzCm1heF96XzEgPC0gbWF4KHRlbXAkei53aGl0ZW5lZC4xKQptYXhfel8yIDwtIG1heCh0ZW1wJHoud2hpdGVuZWQuMikKbGltIDwtIHJvdW5kKAogIGlmZWxzZShtYXhfel8yIDwgbWF4X3pfMSwgbWF4X3pfMSwgbWF4X3pfMiksCiAgZGlnaXRzID0gMCkKcm0obWF4X3pfMSwgbWF4X3pfMikKCiMgZ2V0IHRoZSBnd2FzIFotc2NvcmUKZ3dhc196IDwtIGFicyhxbm9ybSg1ZS04KSkKCmBgYAoKYGBge3J9CgpnZ3B1YnI6Omdnc2NhdHRlcih0ZW1wLAogICAgICAgICAgICAgICAgICB4ID0gInoud2hpdGVuZWQuMSIsCiAgICAgICAgICAgICAgICAgIHkgPSAiei53aGl0ZW5lZC4yIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSB1aXRob2ZfY29sb3JbMTZdLCAKICAgICAgICAgICAgICAgICAgeGxpbSA9IGMoLWxpbSxsaW0pLAogICAgICAgICAgICAgICAgICB5bGltID0gYygtbGltLGxpbSksCiAgICAgICAgICAgICAgICAgIHhsYWIgPSAiQ0FDICh6LCB3aGl0ZW5lZCkiLAogICAgICAgICAgICAgICAgICB5bGFiID0gIkNBRCAoeiwgd2hpdGVuZWQpIikgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBnd2FzX3osIGxpbmV0eXBlID0gMiwKICAgICAgICAgICAgICAgIGNvbG9yID0gdWl0aG9mX2NvbG9yWzNdLCBzaXplID0gMC41KSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWd3YXNfeiwgbGluZXR5cGUgPSAyLAogICAgICAgICAgICAgICAgY29sb3IgPSB1aXRob2ZfY29sb3JbM10sIHNpemUgPSAwLjUpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBnd2FzX3osIGxpbmV0eXBlID0gMiwKICAgICAgICAgICAgICAgIGNvbG9yID0gdWl0aG9mX2NvbG9yWzNdLCBzaXplID0gMC41KSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gLWd3YXNfeiwgbGluZXR5cGUgPSAyLAogICAgICAgICAgICAgICAgY29sb3IgPSB1aXRob2ZfY29sb3JbM10sIHNpemUgPSAwLjUpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgdGhlbWVfbWluaW1hbCgpCnJtKHRlbXApCmBgYAoKV2UgY2FsY3VsYXRlZCB0aGUgY2VudHJhbCBhbmdsZSBiZXR3ZWVuIG9ic2VydmVkIGFuZCBleHBlY3RlZCBTTlAgcG9zaXRpb24gdW5kZXIgdGhlIG51bGwtaHlwb3RoZXNpcyBvZiB0cmFpdC1zcGVjaWZpYyBlZmZlY3RzLCBhbmQgdGhlIHJhZGl1cyAoYHJgKSBpbmRpY2F0aW5nIHRoZSBzaXplIG9mIHRoZSBlZmZlY3QuIAoKTm93IHdlIGNhbGN1bGF0ZSB0aGUgcC12YWx1ZSBmb3IgdGhlIHJhZGl1cywgX2ByYF8uCgpgYGB7cn0KbGlicmFyeShxdmFsdWUpCiMgcC12YWx1ZSAmIHEtdmFsdWUgZm9yIHIKQ0FDX0NBRHRpYmYkci5wdmFsIDwtIFBvbGFyTW9ycGhpc206OlB2YWx1ZUZvclIociA9IENBQ19DQUR0aWJmJHIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwID0gMikKQ0FDX0NBRHRpYmYkci5xdmFsIDwtIHF2YWx1ZShwID0gQ0FDX0NBRHRpYmYkci5wdmFsKSRxdmFsdWVzCmBgYAoKU2Vjb25kLCB3ZSBjYWxjdWxhdGUgdGhlIHRoZSBWb24gTWlzZXMgZGlzdHJpYnV0aW9uIHAtdmFsdWUgZm9yIHRoZSBhbmdsZSwgX2B0aGV0YWBfLCBidXQgb25seSBmb3IgdGhlIHN1YnNldCBvZiB2YXJpYW50cyB3aGVyZSB0aGUgZmFsc2UtZGlzY292ZXJ5IHJhdGUgcS12YWx1ZSBpcyBzaWduaWZpY2FudCwgYHEgPCAwLjA1YC4KCmBgYHtyfQojIGZpbHRlciBvbiByLCBmb3IgcC12YWx1ZSAmIHEtdmFsdWUgZm9yIHRoZXRhCgpQb2xhck1vcnBoaWVzIDwtIENBQ19DQUR0aWJmW0NBQ19DQUR0aWJmJHIucXZhbCA8IDAuMDUsXQoKUG9sYXJNb3JwaGllcyR0aGV0YS5wdmFsIDwtIFBvbGFyTW9ycGhpc206OlB2YWx1ZUZvckFuZ2xlKGFuZ2xlLnRyYW5zID0gUG9sYXJNb3JwaGllcyRhbmdsZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByID0gUG9sYXJNb3JwaGllcyRyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0b2wgPSAxZS0yMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGthcHBhLmZpbGUgPSBwYXN0ZTAoUFJPSkVDVF9sb2MsICIvUG9sYXJNb3JwaGlzbS9rYXBwYXMuNGZvbGR0cmFuc2Zvcm0uUmRhIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWJ1ZyA9IEZBTFNFKQojIHNvbWUgdGhldGEucHZhbCBhcmUgc21hbGxlciB0aGFuIDAKUG9sYXJNb3JwaGllcyR0aGV0YS5wdmFsW1BvbGFyTW9ycGhpZXMkdGhldGEucHZhbCA8IDBdIDwtIDAgCgojIG1pbihQb2xhck1vcnBoaWVzJHRoZXRhLnB2YWwpCgpQb2xhck1vcnBoaWVzJHRoZXRhLnF2YWwgPC0gcXZhbHVlKHAgPSBQb2xhck1vcnBoaWVzJHRoZXRhLnB2YWwpJHF2YWx1ZXMKClBvbGFyTW9ycGhpZXMgPC0gUG9sYXJNb3JwaGllcyAlPiUKICBtdXRhdGUoVGhldGFRID0gY2FzZV93aGVuKHRoZXRhLnF2YWwgPCAwLjA1ICYgcHZhbC4xID4gNWUtOCAmIHB2YWwuMiA+IDVlLTh+ICdub3ZlbCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGV0YS5xdmFsIDwgMC4wNSAmIHB2YWwuMSA8IDVlLTggJiBwdmFsLjIgPiA1ZS04fiAnc2lnbi4gR1dBUyBDQUMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhldGEucXZhbCA8IDAuMDUgJiBwdmFsLjEgPiA1ZS04ICYgcHZhbC4yIDwgNWUtOH4gJ3NpZ24uIEdXQVMgQ0FEJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAnbm90IHNpZ25pZmljYW50JykpCgpgYGAKCmBgYHtyfQojIGZpbHRlciBvbiB0aGV0YQpQb2xhck1vcnBoaWVzICU+JQogIGdncGxvdChhZXMoeCA9IGFicyhhbmdsZSksIHkgPSByLCBjb2xvciA9IHRoZXRhLnF2YWwgPCAwLjA1KSkgKwogIGdlb21fcG9pbnQoKQoKClBvbGFyTW9ycGhpZXMgJT4lCiAgZ2dwbG90KGFlcyh4ID0gYWJzKHoud2hpdGVuZWQuMSksIHkgPSBhYnMoei53aGl0ZW5lZC4yKSwgY29sb3IgPSB0aGV0YS5xdmFsIDwgMC4wNSkpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgeGxpbSgwLGxpbSkgKwogIHlsaW0oMCxsaW0pICsKICBnZW9tX3BvaW50KCkKClBvbGFyTW9ycGhpZXMkYWJzLnoud2hpdGVuZWQuMSA8LSBhYnMoUG9sYXJNb3JwaGllcyR6LndoaXRlbmVkLjEpClBvbGFyTW9ycGhpZXMkYWJzLnoud2hpdGVuZWQuMiA8LSBhYnMoUG9sYXJNb3JwaGllcyR6LndoaXRlbmVkLjIpCgpnZ3B1YnI6Omdnc2NhdHRlcihQb2xhck1vcnBoaWVzLAogICAgICAgICAgICAgICAgICB4ID0gImFicy56LndoaXRlbmVkLjEiLAogICAgICAgICAgICAgICAgICB5ID0gImFicy56LndoaXRlbmVkLjIiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJUaGV0YVEiLCBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIHhsaW0gPSBjKDAsbGltKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCxsaW0pLAogICAgICAgICAgICAgICAgICB4bGFiID0gIkNBQ1xuKHosIHdoaXRlbmVkLCBhYnMpIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJDQURcbih6LCB3aGl0ZW5lZCwgYWJzKSIpICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gZ3dhc196LCBsaW5ldHlwZSA9IDIsCiAgICAgICAgICAgICAgICBjb2xvciA9IHVpdGhvZl9jb2xvclszXSwgc2l6ZSA9IDAuNSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGd3YXNfeiwgbGluZXR5cGUgPSAyLAogICAgICAgICAgICAgICAgY29sb3IgPSB1aXRob2ZfY29sb3JbM10sIHNpemUgPSAwLjUpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAxKSArCiAgdGhlbWVfbWluaW1hbCgpCgoKCmBgYAoKYGBge3J9ClBvbGFyTW9ycGhpZXMKYGBgCgojIyBQcnVuZSBzaWduaWZpY2FudCB2YXJpYW50cwoKYGBge3J9CgojIGZpeCBzbyB0aGF0IGlldWd3YXNyOjpsZF9jbHVtcCB3aWxsIHdvcmsKUG9sYXJNb3JwaGllcyA8LSByZW5hbWUoUG9sYXJNb3JwaGllcywgcnNpZCA9IHNucGlkKQpQb2xhck1vcnBoaWVzIDwtIHJlbmFtZShQb2xhck1vcnBoaWVzLCBwdmFsID0gdGhldGEucXZhbCkgIyB3ZSB3YW50IHRvIGNsdW1wIG9uIHRoZSB0aGV0YS5xdmFsCgpQb2xhck1vcnBoaWVzTERjbHVtcCA8LSBpZXVnd2Fzcjo6bGRfY2x1bXAoCiAgZGF0ID0gUG9sYXJNb3JwaGllcywgCiAgY2x1bXBfa2IgPSAxMDAwMCwgCiAgY2x1bXBfcjIgPSAwLjAwMSwgCiAgY2x1bXBfcCA9IDEsIAogIHBvcCA9ICJFVVIiLCAKICBhY2Nlc3NfdG9rZW4gPSBOVUxMCiAgIyBhY2Nlc3NfdG9rZW4gPSBOVUxMLAogICMgYmZpbGUgPSBOVUxMLAogICMgcGxpbmtfYmluID0gTlVMTAogICkKCiMgZml4IHNvIHRoYXQgUG9sYXJNb3JwaGllcyBhcmUgY29ycmVjdCBhZ2FpbgpQb2xhck1vcnBoaWVzIDwtIHJlbmFtZShQb2xhck1vcnBoaWVzLCBzbnBpZCA9IHJzaWQpClBvbGFyTW9ycGhpZXMgPC0gcmVuYW1lKFBvbGFyTW9ycGhpZXMsIHRoZXRhLnF2YWwgPSBwdmFsKQoKUG9sYXJNb3JwaGllc0xEY2x1bXAgPC0gcmVuYW1lKFBvbGFyTW9ycGhpZXNMRGNsdW1wLCBzbnBpZCA9IHJzaWQpClBvbGFyTW9ycGhpZXNMRGNsdW1wIDwtIHJlbmFtZShQb2xhck1vcnBoaWVzTERjbHVtcCwgdGhldGEucXZhbCA9IHB2YWwpCgpQb2xhck1vcnBoaWVzTERjbHVtcAoKYGBgCgojIyMgQW5ub3RhdGlvbgoKYGBge3J9CgojIFJlZmVyZW5jZTogaHR0cHM6Ly9iaW9pbmZvcm1hdGljcy5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvMjUwMy9ob3ctdG8tZ2V0LWEtbGlzdC1vZi1nZW5lcy1jb3JyZXNwb25kaW5nLXRvLXRoZS1saXN0LW9mLXNucHMtcnMtaWRzLzI1MDQKCnJlcXVpcmUoYmlvbWFSdCkKCiMgVG8gc2hvdyB3aGljaCBtYXJ0cyBhcmUgYXZhaWxhYmxlCmxpc3RNYXJ0cygpCgojIFlvdSBuZWVkIHRoZSBTTlAgbWFydAptYXJ0IDwtIHVzZU1hcnQoIkVOU0VNQkxfTUFSVF9TTlAiKQoKIyBGaW5kIGhvbW8gc2FwaWVucwpsaXN0RGF0YXNldHMobWFydCkKCiMgVGhpcyB3aWxsIGJlIHRoZSBkYXRhc2V0IHdlIHdhbnQgdG8gdXNlCmRhdGFzZXQgPC0gdXNlRGF0YXNldCgiaHNhcGllbnNfc25wIiwgbWFydCA9IG1hcnQpCgojIFNob3cgYXZhaWxhYmxlIGZpbHRlcnMKbGlzdEZpbHRlcnMoZGF0YXNldCkKCiMgTm93IGxpc3QgYWxsIGF2YWlsYWJsZSBhdHRyaWJ1dGVzCmxpc3RBdHRyaWJ1dGVzKGRhdGFzZXQpCgojIFlvdSBuZWVkIHRoZSBTTlAgbWFydCBmcm9tIGhvbW8gc2FwaWVucwplbnNlbWJsIDwtIHVzZU1hcnQoIkVOU0VNQkxfTUFSVF9TTlAiLCBkYXRhc2V0ID0gImhzYXBpZW5zX3NucCIpCmBgYAoKYGBge3J9CiMgVG8gZ2V0IHRoZSBlbnNlbWJsIGdlbmUgaWQgYmVsb25naW5nIHRvIHRoZSBTTlBzCkFubm90YXRpb25zIDwtIGdldEJNKGF0dHJpYnV0ZXMgPSBjKCJyZWZzbnBfaWQiLCAiY2hyX25hbWUiLCAiY2hyb21fc3RhcnQiLCAiY2hyb21fZW5kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYWxsZWxlIiwgIm1hcHdlaWdodCIsICJ2YWxpZGF0ZWQiLCAiYWxsZWxlXzEiLCAibWlub3JfYWxsZWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibWlub3JfYWxsZWxlX2ZyZXEiLCAibWlub3JfYWxsZWxlX2NvdW50IiwgImNsaW5pY2FsX3NpZ25pZmljYW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN5bm9ueW1fbmFtZSIsICJyZWZzbnBfc291cmNlIiwgImVuc2VtYmxfZ2VuZV9zdGFibGVfaWQiLCAiZW5zZW1ibF9nZW5lX25hbWUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXJzID0gInNucF9maWx0ZXIiLCB2YWx1ZXMgPSBQb2xhck1vcnBoaWVzTERjbHVtcCRzbnBpZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJ0ID0gZW5zZW1ibCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pcXVlUm93cyA9IFRSVUUpCkFubm90YXRpb25zCgpgYGAKCgpgYGB7cn0KZndyaXRlKFBvbGFyTW9ycGhpZXNMRGNsdW1wLCBmaWxlID0gcGFzdGUwKE9VVF9sb2MsICIvUG9sYXJNb3JwaGllc0xEY2x1bXAudHh0IiksIHNlcCA9ICJcdCIpCmBgYAoKYGBge3J9CnByb3h5U2VhcmNoX0NBQ19DQUQgPC0gYXNfdGliYmxlKGZyZWFkKGZpbGUgPSBwYXN0ZTAoUFJPSkVDVF9sb2MsICIvUG9sYXJNb3JwaGlzbS9wcm94eVNlYXJjaF9DQUNfQ0FEL3Byb3h5U2VhcmNoLnJlc3VsdHMuY3N2IikpKQoKcHJveHlTZWFyY2hfQ0FDX0NBRApgYGAKCmBgYHtyfQpQb2xhck1vcnBoaWVzTERjbHVtcEEgPC0gbWVyZ2UoUG9sYXJNb3JwaGllc0xEY2x1bXAsIHByb3h5U2VhcmNoX0NBQ19DQUQsIGJ5LnggPSAic25waWQiLCBieS55ID0gIlJTSUQiLCBzb3J0ID0gRkFMU0UsIGFsbC54ID0gVFJVRSkKYGBgCgoKIyMjIFBsb3R0aW5nCmBgYHtyfQoKbm92ZWxfc25wcyA8LSBQb2xhck1vcnBoaWVzTERjbHVtcCRzbnBpZFtncmVwbCgibm92ZWwiLCBQb2xhck1vcnBoaWVzTERjbHVtcCRUaGV0YVEpXQoKbGlicmFyeShnZ3B1YnIpCnAxIDwtIGdncHVicjo6Z2dzY2F0dGVyKFBvbGFyTW9ycGhpZXNMRGNsdW1wLAogICAgICAgICAgICAgICAgICB4ID0gImFicy56LndoaXRlbmVkLjEiLAogICAgICAgICAgICAgICAgICB5ID0gImFicy56LndoaXRlbmVkLjIiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJUaGV0YVEiLCBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIHhsaW0gPSBjKDAsbGltKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCxsaW0pLAogICAgICAgICAgICAgICAgICB4bGFiID0gIkNBQ1xuKHosIHdoaXRlbmVkLCBhYnMpIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJDQURcbih6LCB3aGl0ZW5lZCwgYWJzKSIsCiAgICAgICAgICAgICAgICAgIHRpdGxlID0gIkNBQyB2cyBDQUQiLAogICAgICAgICAgICAgICAgICBsYWJlbCA9ICJzbnBpZCIsCiAgICAgICAgICAgICAgICAgIGxhYmVsLnNlbGVjdCA9IG5vdmVsX3NucHMsCiAgICAgICAgICAgICAgICAgIHJlcGVsID0gVFJVRSwgc2hvdy5sZWdlbmQudGV4dCA9IEZBTFNFKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGd3YXNfeiwgbGluZXR5cGUgPSAyLAogICAgICAgICAgICAgICAgY29sb3IgPSB1aXRob2ZfY29sb3JbM10sIHNpemUgPSAwLjUpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBnd2FzX3osIGxpbmV0eXBlID0gMiwKICAgICAgICAgICAgICAgIGNvbG9yID0gdWl0aG9mX2NvbG9yWzNdLCBzaXplID0gMC41KSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKCnAyIDwtIGdncHVicjo6Z2dzY2F0dGVyKFBvbGFyTW9ycGhpZXNMRGNsdW1wLAogICAgICAgICAgICAgICAgICB4ID0gImFicy56LndoaXRlbmVkLjEiLAogICAgICAgICAgICAgICAgICB5ID0gImFicy56LndoaXRlbmVkLjIiLAogICAgICAgICAgICAgICAgICBjb2xvciA9ICJUaGV0YVEiLCBwYWxldHRlID0gIm5wZyIsCiAgICAgICAgICAgICAgICAgIHhsaW0gPSBjKDAsbGltLzMpLAogICAgICAgICAgICAgICAgICB5bGltID0gYygwLGxpbS8zKSwKICAgICAgICAgICAgICAgICAgeGxhYiA9ICJDQUNcbih6LCB3aGl0ZW5lZCwgYWJzKSIsCiAgICAgICAgICAgICAgICAgIHlsYWIgPSAiQ0FEXG4oeiwgd2hpdGVuZWQsIGFicykiLAogICAgICAgICAgICAgICAgICB0aXRsZSA9ICJDQUMgdnMgQ0FEICh6b29tZWQpIiwKICAgICAgICAgICAgICAgICAgbGFiZWwgPSAic25waWQiLAogICAgICAgICAgICAgICAgICBsYWJlbC5zZWxlY3QgPSBub3ZlbF9zbnBzLAogICAgICAgICAgICAgICAgICByZXBlbCA9IFRSVUUsIHNob3cubGVnZW5kLnRleHQgPSBGQUxTRSkgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBnd2FzX3osIGxpbmV0eXBlID0gMiwKICAgICAgICAgICAgICAgIGNvbG9yID0gdWl0aG9mX2NvbG9yWzNdLCBzaXplID0gMC41KSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gZ3dhc196LCBsaW5ldHlwZSA9IDIsCiAgICAgICAgICAgICAgICBjb2xvciA9IHVpdGhvZl9jb2xvclszXSwgc2l6ZSA9IDAuNSkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCgpwMQoKZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKFBMT1RfbG9jLCAiLyIsIFRvZGF5LCAiLlBvbGFyTW9ycGhpZXMuTERjbHVtcC5DQUMuQ0FELnBkZiIpLCBwbG90ID0gcDEpCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi5Qb2xhck1vcnBoaWVzLkxEY2x1bXAuQ0FDLkNBRC5wbmciKSwgcGxvdCA9IHAxKQoKcDIKCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi5Qb2xhck1vcnBoaWVzLkxEY2x1bXAuQ0FDLkNBRC56b29tLnBkZiIpLCBwbG90ID0gcDIpCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChQTE9UX2xvYywgIi8iLCBUb2RheSwgIi5Qb2xhck1vcnBoaWVzLkxEY2x1bXAuQ0FDLkNBRC56b29tLnBuZyIpLCBwbG90ID0gcDIpCgoKYGBgCgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcy5hdXRvKHBsb3RseSkKbGlicmFyeShwbG90bHkpCgpwMyA8LSBnZ3B1YnI6Omdnc2NhdHRlcihQb2xhck1vcnBoaWVzTERjbHVtcCwKICAgICAgICAgICAgICAgICAgeCA9ICJhYnMuei53aGl0ZW5lZC4xIiwKICAgICAgICAgICAgICAgICAgeSA9ICJhYnMuei53aGl0ZW5lZC4yIiwKICAgICAgICAgICAgICAgICAgY29sb3IgPSAiVGhldGFRIiwgCiAgICAgICAgICAgICAgICAgIHBhbGV0dGUgPSAibnBnIiwKICAgICAgICAgICAgICAgICAgeGxpbSA9IGMoMCxsaW0vMyksCiAgICAgICAgICAgICAgICAgIHlsaW0gPSBjKDAsbGltLzMpLAogICAgICAgICAgICAgICAgICB4bGFiID0gIkNBQ1xuKHosIHdoaXRlbmVkLCBhYnMpIiwKICAgICAgICAgICAgICAgICAgeWxhYiA9ICJDQURcbih6LCB3aGl0ZW5lZCwgYWJzKSIsCiAgICAgICAgICAgICAgICAgIHRpdGxlID0gIkNBQyB2cyBDQUQgKHpvb21lZCkiKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGd3YXNfeiwgbGluZXR5cGUgPSAyLAogICAgICAgICAgICAgICAgY29sb3IgPSB1aXRob2ZfY29sb3JbM10sIHNpemUgPSAwLjUpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBnd2FzX3osIGxpbmV0eXBlID0gMiwKICAgICAgICAgICAgICAgIGNvbG9yID0gdWl0aG9mX2NvbG9yWzNdLCBzaXplID0gMC41KSArCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKwogIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKZ2dwbG90bHkocDMsIAogICAgICAgICBzb3VyY2UgPSAic2VsZWN0IiwgCiAgICAgICAgIHRvb2x0aXAgPSBjKCJhbGwiKSkKCgpgYGAKCgpgYGB7cn0KbGlicmFyeShwbG90bHkpCgojIGh0dHBzOi8vcGxvdGx5LmNvbS9yL2hvcml6b250YWwtdmVydGljYWwtc2hhcGVzLwoKdmxpbmUgPC0gZnVuY3Rpb24oeCA9IDAsIGNvbG9yID0gdWl0aG9mX2NvbG9yWzNdKSB7CiAgbGlzdCgKICAgIHR5cGUgPSAibGluZSIsCiAgICB5MCA9IDAsCiAgICB5MSA9IDEsCiAgICB5cmVmID0gInBhcGVyIiwKICAgIHgwID0geCwKICAgIHgxID0geCwKICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gY29sb3IsIGRhc2g9ImRvdCIpCiAgKQp9CgpobGluZSA8LSBmdW5jdGlvbih5ID0gMCwgY29sb3IgPSB1aXRob2ZfY29sb3JbM10pIHsKICBsaXN0KAogICAgdHlwZSA9ICJsaW5lIiwKICAgIHgwID0gMCwKICAgIHgxID0gMSwKICAgIHhyZWYgPSAicGFwZXIiLAogICAgeTAgPSB5LAogICAgeTEgPSB5LAogICAgbGluZSA9IGxpc3QoY29sb3IgPSBjb2xvciwgZGFzaD0iZG90IikKICApCn0KCiMgaHR0cHM6Ly9wbG90bHkuY29tL3IvaG92ZXItdGV4dC1hbmQtZm9ybWF0dGluZy8KCmZpZyA8LSBQb2xhck1vcnBoaWVzTERjbHVtcCAlPiUKICBwbG90X2x5KAogICAgdHlwZSA9ICJzY2F0dGVyIiwKICAgIG1vZGUgPSAnbWFya2VycycsCiAgICB4ID0gfmFicy56LndoaXRlbmVkLjEsIAogICAgeSA9IH5hYnMuei53aGl0ZW5lZC4yLAogICAgbWFya2VyID0gbGlzdChzaXplID0gfnIsIHNpemVyZWYgPSAwLjgpLCAKICAgIGNvbG9yID0gflRoZXRhUSwgY29sb3JzID0gdWl0aG9mX2NvbG9yLAogICAgdGV4dCA9IH5zbnBpZCwKICAgIGhvdmVydGVtcGxhdGUgPSBwYXN0ZSgKICAgICAgIjxiPiV7dGV4dH08L2I+PGJyPjxicj4iLAogICAgICAiJXt5YXhpcy50aXRsZS50ZXh0fTogJXt5fSAoYWJzLiwgd2hpdGVuZWQpPGJyPiIsCiAgICAgICIle3hheGlzLnRpdGxlLnRleHR9OiAle3h9IChhYnMuLCB3aGl0ZW5lZCk8YnI+IiwKICAgICAgIjxleHRyYT48L2V4dHJhPiIKICAgICAgKQogICAgKSAlPiUKICBsYXlvdXQodGl0bGUgPSAnUG9sYXJNb3JwaGlzbTogQ0FDIHZzIENBRCcsIAogICAgICAgICBwbG90X2JnY29sb3IgPSAid2hpdGUiLAogICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiQ0FDIiksIAogICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiQ0FEIiksIAogICAgICAgICBsZWdlbmQgPSBsaXN0KHRpdGxlPWxpc3QodGV4dD0nPGI+UGxlaW90cm9waWMgZWZmZWN0IDwvYj4nKSksCiAgICAgICAgIHNoYXBlcyA9IGxpc3QodmxpbmUoZ3dhc196KSwgaGxpbmUoZ3dhc196KSAjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBsaXN0KHR5cGUgPSAicmVjdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgICAgZmlsbGNvbG9yID0gdWl0aG9mX2NvbG9yWzI4XSwgbGluZSA9IGxpc3QoY29sb3IgPSB1aXRob2ZfY29sb3JbMjddKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgICAgb3BhY2l0eSA9IDAuMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICB5MCA9IG5vbV96X3ksIHkxID0gZ3dhc196LCB4MCA9IG5vbV96X3gsIHgxID0gZ3dhc196KQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIyApCiAgICAgICAgICAgICAgICAgICAgICAgKQogICkKCmZpZyA8LSBmaWcgJT4lCiAgbGF5b3V0KGxlZ2VuZCA9IGxpc3Qob3JpZW50YXRpb24gPSAnaCcsIHkgPSAtMC4zKSkKCmZpZwoKYGBgCgoKIyBTZXNzaW9uIGluZm9ybWF0aW9uCgotLS0tLS0KCiAgICBWZXJzaW9uOiAgICAgIHYxLjAuMAogICAgTGFzdCB1cGRhdGU6ICAyMDIyLTAyLTAzCiAgICBXcml0dGVuIGJ5OiAgIFNhbmRlciBXLiB2YW4gZGVyIExhYW4gKHMudy52YW5kZXJsYWFuLTJbYXRddW1jdXRyZWNodC5ubCkuCiAgICBEZXNjcmlwdGlvbjogIFNjcmlwdCB0byBjcmVhdGUgcGxvdCByZWdpb25hbCBhc3NvY2lhdGlvbiBwbG90cy4KICAgIE1pbmltdW0gcmVxdWlyZW1lbnRzOiBSIHZlcnNpb24gMy40LjMgKDIwMTctMDYtMzApIC0tICdTaW5nbGUgQ2FuZGxlJywgTWFjIE9TIFggRWwgQ2FwaXRhbgogICAgCiAgICBDaGFuZ2VzIGxvZwogICAgKiB2MS4wLjAgSW5pdGlhbCB2ZXJzaW9uLiAKCi0tLS0tLQoKYGBge3IgZXZhbCA9IFRSVUV9CnNlc3Npb25JbmZvKCkKYGBgCgoKIyBTYXZpbmcgZW52aXJvbm1lbnQKYGBge3IgU2F2aW5nfQoKc2F2ZS5pbWFnZShwYXN0ZTAoUFJPSkVDVF9sb2MsICIvIixUb2RheSwiLiIsUFJPSkVDVE5BTUUsIi5Qb2xhck1vcnBoaXNtLlJEYXRhIikpCmBgYAoKCi0tLS0tLQo8c3VwPiZjb3B5OyAxOTc5LTIwMjIgU2FuZGVyIFcuIHZhbiBkZXIgTGFhbiB8IHMudy52YW5kZXJsYWFuW2F0XWdtYWlsLmNvbSB8IFtzd3ZhbmRlcmxhYW4uZ2l0aHViLmlvXShodHRwczovL3N3dmFuZGVybGFhbi5naXRodWIuaW8pLjwvc3VwPgotLS0tLS0KCiAgCg==